BZOJ 2759 一个动态树好题(Link-Cut Tree+数学)
来源:互联网 发布:笑气在淘宝上叫什么 编辑:程序博客网 时间:2024/06/05 10:46
2759: 一个动态树好题
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 772 Solved: 258
[Submit][Status][Discuss]
Description
有N个未知数x[1..n]和N个等式组成的同余方程组:
x[i]=k[i]*x[p[i]]+b[i] mod 10007
其中,k[i],b[i],x[i]∈[0,10007)∩Z
你要应付Q个事务,每个是两种情况之一:
一.询问当前x[a]的解
A a
无解输出-1
x[a]有多解输出-2
否则输出x[a]
二.修改一个等式
C a k[a] p[a] b[a]
Input
N 下面N行,每行三个整数k[i] p[i] b[i]
Q 下面Q行,每行一个事务,格式见题目描述
Output
对每个询问,输出一行一个整数。
对100%的数据,1≤N≤30000,0≤Q≤100000,时限2秒,其中询问事务约占总数的80%
Sample Input
5
2 2 1
2 3 2
2 4 3
2 5 4
2 3 5
5
A 1
A 2
C 5 3 1 1
A 4
A 5
2 2 1
2 3 2
2 4 3
2 5 4
2 3 5
5
A 1
A 2
C 5 3 1 1
A 4
A 5
Sample Output
4276
7141
4256
2126
7141
4256
2126
HINT
Source
By 范浩强
中文题就不解释题意了……
做一道LCT主要是想在赛前复习一下LCT,然后再次感叹LCT的神奇……
这题很多同余方程,某个方程的解依赖于其他方程的解,由此可以构成一个类似于树的数据结构,所以比较自然的想到LCT。然后根据式子的关系,显然这些方程是可以相互合并的(把一个方程代入另一个方程),如此更适合用数据结构了。但是,问题来了,这里面可能出现环,该如何处理呢。我们注意到,父亲可以由儿子表示,爷爷可以由父亲表示,那么当出现环的时候,我完全可以通过合并,使得假想根和它的父亲fa(它的父亲也是它的后代)用同一个x表示。于是出现这种情况相当于x=k*x+b,相当于解这样的一个方程,这个就小菜一碟啦。所以说,我们可以选定一个假想根,然后断开它与它的父亲,但是要把它的父亲给记录下来,之后就是一颗完整的树。那么经过这样操作之后,就会形成一个森林。具体来说,我们可以看下面那个图来理解。
其中,虚线那条边表示被删掉的边,如此一堆关系可以形成一个森林。
然后,我们再考虑每一种操作。对于询问方程的结果,我们按照上面说的方法来求解。首先,对于要求的点x,它的解等于k[x]*根的解+b[x]。那么相当于要求根的解,而根的解=k[fa]*根的解+b[fa],这个fa就是我们之前删掉的那个父亲,在这时起作用。解这个方程就可以求出结果。
接着我们开修改操作。所谓修改主要就是考虑断开和重新连接嘛。这时,我们就要分情况讨论一下了,我们设修改的点为x,与之有关的方程是f。
如果x根而且f的根不是x,那么其他不管,直接把它的父亲变为f即可;
如果f的根也是x,这意味这什么呢?意味着构成了一个新的环,所以我们从这断开,并把这个父亲保存下来;
如果x不是根,那么显然关联方程改变,对应的与父亲的连边肯定是要断开的,然后还要再讨论一下啦,如果x在环上(可以看上图,不是所有点都在环上),那么就会对原来整个环产生影响,原本的环断开变成了真正的链,意味着虚拟断开的边不需要再断开了,把根的父亲变为原本断开的父亲即可;如果x不在环上,那么仅仅断开就行了,其他不影响;
这便是两种操作。然后关于LCT本身,还有要说的。因为我们保存的元素是方程的两个系数k和b,所以我们在定义的时候定义一个结构重载加法。然后很明显可以发现,这个重载的加法并不满足交换律,所以我们在合并的时候要格外小心,注意顺序。还有,就是LCT的beroot操作不要随便乱用,至少在这题是这样的。具体见代码:
#include<bits/stdc++.h>#define mod 10007#define N 30010using namespace std;struct line{ int k,b; friend line operator + (const line a,const line b)//重载加法 {return line{a.k*b.k%mod,(b.b+b.k*a.b)%mod};}};int n,m,t,inv[N],rt[N],v[N];struct Link_Cut_Tree{ int son[N][2],fa[N]; line num[N],sum[N]; inline bool which(int x){return son[fa[x]][1]==x;} bool isroot(int x){return !fa[x]||son[fa[x]][which(x)]!=x;} inline void push_up(int x) { if (!x) return; sum[x]=num[x]; if (son[x][0]) sum[x]=sum[son[x][0]]+sum[x];//注意合并顺序,不能错 if (son[x][1]) sum[x]=sum[x]+sum[son[x][1]]; } inline void Rotate(int x) { int y=fa[x]; bool ch=which(x); son[y][ch]=son[x][ch^1];son[x][ch^1]=y; if (!isroot(y)) son[fa[y]][which(y)]=x; fa[x]=fa[y]; fa[y]=x; fa[son[y][ch]]=y; push_up(y); push_up(x); } inline void splay(int x) { while (!isroot(x)) { int y=fa[x]; if (!isroot(y)) { if (which(x)^which(y)) Rotate(x); else Rotate(y); } Rotate(x); } } inline void access(int x) { int y=0; while (x) { splay(x); son[x][1]=y; push_up(x); y=x; x=fa[x]; } } inline int getroot(int x) { access(x); splay(x); while (son[x][0]) x=son[x][0]; splay(x); return x; } inline void cut(int x) { access(x); splay(x); son[x][0]=fa[son[x][0]]=0; push_up(x); } inline bool judge(int x,int y)//判定x,是否在y为根的环的环上 { access(rt[y]); splay(rt[y]); splay(x);//如果在环上,那么在access(rt[y])之后再splay(x) return x==rt[y]||!isroot(rt[y]);//rt[y]一定不是根,或者x本身就是rt[y] }} LCT;void init(){ inv[1]=1; for(int i=2;i<N;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;//线性求逆元,解方程要用 memset(rt,0,sizeof(rt));}void dfs(int x){ if (v[x]) return; v[x]=t; if (v[LCT.fa[x]]==t) { rt[x]=LCT.fa[x]; LCT.fa[x]=0; } else dfs(LCT.fa[x]);}int main(){ init(); scanf("%d",&n); for(int i=1;i<=n;i++) { int k,f,b; scanf("%d%d%d",&k,&f,&b); LCT.fa[i]=f; LCT.num[i]=line{k,b}; } for(int i=1;i<=n;i++) if (!v[i]) t++,dfs(i); scanf("%d",&m); while(m--) { char op[5]; int x,k,f,b,Rt; scanf("%s",op); if (op[0]=='A') { scanf("%d",&x); LCT.access(x); LCT.splay(x); line a=LCT.sum[x]; Rt=LCT.getroot(x); LCT.access(rt[Rt]); LCT.splay(rt[Rt]); line b=LCT.sum[rt[Rt]]; if (b.k==1&&a.k!=0)//判定多组解和无解 { if (b.b) puts("-1"); else puts("-2"); } else { int ans=b.b*inv[(1-b.k+mod)%mod]%mod;//求根的解 printf("%d\n",(a.k*ans+a.b)%mod);//求x的解 } } else { scanf("%d%d%d%d",&x,&k,&f,&b); LCT.access(x); LCT.splay(x); LCT.num[x]=line{k,b}; LCT.push_up(x); Rt=LCT.getroot(x); if (Rt!=x)//x不为根 { LCT.cut(x); if (LCT.judge(x,Rt))//判定x是否在环上 { LCT.access(Rt); LCT.splay(Rt); LCT.fa[Rt]=rt[Rt]; rt[Rt]=0;//在的话把rt变为真正意义上的fa } } if (LCT.getroot(f)==x) rt[x]=f;//如果f的根与x相同,那么相当于构成了一个新的环 else LCT.fa[x]=f; } } return 0;}
阅读全文
0 0
- BZOJ 2759 一个动态树好题(Link-Cut Tree+数学)
- BZOJ 2759 一个动态树好题 Link-Cut-Tree+扩展欧几里得
- bzoj 2002 link cut tree(LCT)
- BZOJ 2631 tree 动态树(Link-Cut-Tree)
- BZOJ 3282 Tree Link-Cut-Tree 动态树
- bzoj 2049(link cut tree)
- bzoj 2157(link cut tree)
- bzoj 1180(link cut tree)
- bzoj 2631(link cut tree)
- BZOJ 2002 Link-Cut Tree
- Link Cut Tree(动态树)
- BZOJ-2049 Cave洞穴勘测 动态树Link-Cut-Tree (并查集骗分TAT)
- BZOJ-3282 Tree Link-Cut-Tree(似乎树链剖分亦可)
- 动态树 Link Cut Tree
- BZOJ 2631 Tree Link-Cut-Tree(LCT)
- BZOJ 3282 Tree Link-Cut-Tree(LCT)
- BZOJ-2631 tree Link-Cut-Tree
- bzoj 2631 Tree [Link-Cut Tree]
- python数据描述(1)
- Java源码的理解
- 项目中tencent.bugly 的配置
- N*N顺时针螺旋递增数组(多益网络)
- 使用tomcat搭建java接口web service步骤
- BZOJ 2759 一个动态树好题(Link-Cut Tree+数学)
- perl语言中的.pm文件和.pl文件区别
- 设计模式--适配器模式(JDK中的应用)
- 安卓知识图谱
- CodeForces 703 C.Chris and Road(贪心)
- Node.js 连接MongoDB,并实现浏览器GET请求加参数
- android7.1编译第三方apk到系统中的android.mk学习 基于packages/apps/Browser/Android.mk文件内容,如下: LOCAL_PATH := $(my-d
- TensorFlow入门教程:2:概述
- python基础语法