BZOJ 2759 一道动态树的好题
2016-05-16 17:45
267 查看
一道好题的标准:
从常规的知识点中敲击出新火花,但并不是偏题。
不卡常,不卡溢出……
首先看一个弱化的问题,一个固定的nn元nn项模方程怎么求解每个变量。高斯消元? 太慢啦!!
如果我们把x→pxx \rightarrow p_x 连边,我们可以得到一个基环森林。对于每个联通块,找到环并求出环中某一个变量的值,显然当一个变量的值求出来了,整个联通块就Okay啦。
接下来我们考虑修改操作,考虑到这玩意是个基环森林,一个常见的思路是拆环成树。所以我们考虑用 LCTLCT 来维护一些信息。
但当务之急是如何处理我们忽略掉的那些边!我们让每个联通块的根节点是这种边的某一端点,然后每个根节点记录这条边的另一端点, 我们叫他SpecialFatherSpecial Father。
SplaySplay 中每条树链 (x→yx \rightarrow y ,其中xx 的深度较浅) 维护一个二元组(k , b):
令:f=x→fa令:f = x\rightarrow fa
y=f×k+by = f\times k + b
就是如何用xx的父亲表示y。(其中根节点的父亲就是那个神奇的SpecialFatherSpecial Father)
那么不难想到如果用将根节点和SpecialFatherSpecial Father的树链调出来,那么就可以求出SpecialFatherSpecial Father 的值啦!
其实SpecialFatherSpecial Father 的存在并不影响所有的 LCTLCT 的操作。但是要注意在修改边的时候,要先删边再加边,这样逻辑更清晰。
几点启发:
1. 任何对父亲或者自身的操作之前,都需要LocateLocate
2. LCTLCT记录的量应该只和点权与树链本身有关,而不应该与此时的根节点有任何联系。
3. 在需要利用根节点信息的时候,我们所有操作都不能makeRootmakeRoot
从常规的知识点中敲击出新火花,但并不是偏题。
不卡常,不卡溢出……
首先看一个弱化的问题,一个固定的nn元nn项模方程怎么求解每个变量。高斯消元? 太慢啦!!
如果我们把x→pxx \rightarrow p_x 连边,我们可以得到一个基环森林。对于每个联通块,找到环并求出环中某一个变量的值,显然当一个变量的值求出来了,整个联通块就Okay啦。
接下来我们考虑修改操作,考虑到这玩意是个基环森林,一个常见的思路是拆环成树。所以我们考虑用 LCTLCT 来维护一些信息。
但当务之急是如何处理我们忽略掉的那些边!我们让每个联通块的根节点是这种边的某一端点,然后每个根节点记录这条边的另一端点, 我们叫他SpecialFatherSpecial Father。
SplaySplay 中每条树链 (x→yx \rightarrow y ,其中xx 的深度较浅) 维护一个二元组(k , b):
令:f=x→fa令:f = x\rightarrow fa
y=f×k+by = f\times k + b
就是如何用xx的父亲表示y。(其中根节点的父亲就是那个神奇的SpecialFatherSpecial Father)
那么不难想到如果用将根节点和SpecialFatherSpecial Father的树链调出来,那么就可以求出SpecialFatherSpecial Father 的值啦!
其实SpecialFatherSpecial Father 的存在并不影响所有的 LCTLCT 的操作。但是要注意在修改边的时候,要先删边再加边,这样逻辑更清晰。
几点启发:
1. 任何对父亲或者自身的操作之前,都需要LocateLocate
2. LCTLCT记录的量应该只和点权与树链本身有关,而不应该与此时的根节点有任何联系。
3. 在需要利用根节点信息的时候,我们所有操作都不能makeRootmakeRoot
#include <bits/stdc++.h> using namespace std; const int maxn = 110000; const int modu = 10007; struct line { int k , b; line(int k=1 , int b=0):k(k),b(b){} line operator +(const line& b) { line res; res.k = (k * b.k) % modu; res.b = (b.b + this->b*b.k) % modu; return res; } int F(int x) { return (k * x + b) % modu; } }; struct node { node *ch[2] , *fa , *sfa; line sum , num; int c; void maintain() { sum = ch[0]->sum + num + ch[1]->sum; } }; node *null = new node(); node pool[maxn]; bool isRoot(node *o) { return o->fa->ch[0] != o && o->fa->ch[1] != o; } void rotate(node *&o , int d) { node *k = o->ch[d^1]; k->fa = o->fa; k->ch[d]->fa = o; o->fa = k; o->ch[d^1] = k->ch[d]; k->ch[d] = o; o->maintain(); k->maintain(); o = k; } void _splay(node* &o) { if(o->c != -1) { node *&o2 = o->ch[o->c]; if(o2->c != -1) { _splay(o2->ch[o2->c]); if(o->c == o2->c) rotate(o , o->c^1); else rotate(o2 , o2->c^1);; } rotate(o , o->c^1); } } void splay(node *o) { o->c = -1; while(!isRoot(o)) { if(o->fa->ch[0] == o) o->fa->c = 0; else o->fa->c = 1; o = o->fa; } _splay(o); } node* access(node *x) { node *y = null; while(x != null) { splay(x); x->ch[1] = y; x->maintain(); y = x; x = x->fa; } while(y->ch[0] != null) y = y->ch[0]; return y; } int p[maxn] , book[maxn] , cnt; void dfs(int x) { book[x] = cnt; int f = p[x]; if(book[f] == cnt) pool[x].sfa = pool + f; else pool[x].fa = pool + f; if(!book[f]) dfs(f); } void locate(node *x) { access(x); splay(x); } int inv(int x) { int res = 1 , n = modu - 2; while(n) { if(n&1) (res *= x) %= modu; (x *= x) %= modu; n >>= 1; } return res; } int query(node *x) { node *r = access(x); node *s = r->sfa; locate(s); int k = s->sum.k , b = s->sum.b; if(k == 1) { if(b == 0) return -2; else return -1; } locate(x); return x->sum.F( ((modu - b)*inv(k+modu-1)) % modu ); } void modify(node *x , node *fa , line l) { locate(x); x->num = l; x->maintain(); // first lets cut the past edge node *r = access(x); if(x == r) x->sfa = null; else { locate(x); x->ch[0]->fa = null; x->ch[0] = null; x->maintain(); if(access(r->sfa) != r) locate(r) , r->fa = r->sfa , r->sfa = null; } // then build the new one! // aparently x is a root of some tree if(access(fa) != access(x)) locate(x) , x->fa = fa; else locate(x) , x->sfa = fa; } int re() { int n; scanf("%d" , &n); return n; } int main() { #ifndef ONLINE_JUDGE freopen("in","r",stdin); #endif int n; cin>>n; for(int i=1 , k , b;i<=n;i++) { scanf("%d%d%d" , &k , p+i , &b); pool[i].ch[0] = pool[i].ch[1] = pool[i].fa = pool[i].sfa = null; pool[i].sum = pool[i].num = line(k , b); } for(int i=1;i<=n;i++) if(!book[i]) ++cnt , dfs(i); int q; cin>>q; char op[2]; while(q--) { scanf("%s" , op); if(op[0] == 'A') printf("%d\n" , query(pool + re())); else { int x = re() , k = re() , p = re() , b = re(); modify(pool+x , pool+p , line(k , b)); } } return 0; }
相关文章推荐
- Wireshark图解教程(统计)
- HTML table合并行列后,使用百分比设置列宽
- hbase Java API 介绍及使用示例
- eclipse从数据库逆向生成Hibernate实体类
- 平安好医生技术栈的分析
- Mac OS下PHP环境搭建及PHP操作MySQL常用方法小结
- Nullability、__covariant、__contravariant,__kindof
- android使用ant重新编译打包apk
- form表单中的值设置为disable后出现的问题
- Web服务的性能测试
- JazzyViewPager
- 五十四 网络编程 TCP编程
- Chrome保存mht网页文件的方法(无需插件)
- poj 2114 Boatherds
- Oracle函数
- Shiro--权限控制
- php 实现背景图上添加 圆形logo
- 数学系杀手(数学+找规律)(北理16校赛)
- pch文件添加
- 多线程与信号