2759: 一个动态树好题
Time Limit: 10 Sec Memory Limit: 128 MB
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
Sample Output
4276
7141
4256
2126
确实是道动态树的好题啊= =
以下p=10007。
如果我们把每个i看作是一个点,p(i)看作是i的父亲,显然整个方程组会形成一个基环森林,而每个连通块都是一棵基环外向树,根据经验,我们可以拆掉环上的某条边,因此我们便得到了一个森林,便转化为了动态树的问题(ZGY神犇说可以用仙人掌来做,可是我不会= =)。
首先考虑如何回答询问。显然手动拆掉的那条边的起点是整棵树的根,我们可以对根记一个special father(以下简记为s)表示拆掉那条边的终点,我们设s的值为x,那么对于每个点我们都可得出它的值与x的线性关系。我们找到s后,实际上我们得到了s与它自己的线性关系,即我们得到了一个形如 x≡kx+b(mod p) 的同余方程。那么当k=1时,若b=0则有多组解,反之则无解。其它的,由于p是质数,使用数论知识稍加分析可得该同余方程有且仅有一个解,直接使用扩展欧几里德算法求解即可。注意讨论k=0的情况,因为C语言中负数的除法的特性会使扩展欧几里德算法失效。
修改的话,对于k和b的话access+splay直接改就可以了;修改父亲的时候要分多种情况讨论:如果这个点就是根,那么直接修改它的special father;反之,如果这个点原本在环上,此时我们需要把那条去掉的边补上。当它与它的父亲的边切掉后,它暂时成为了根,与新的父亲连接的时候,如果他们在一棵树上,便形成了一个环,赋值special father即可,否则直接连接。
#include
#include
#define maxn 30000
using namespace std;
const int p=10007;
struct data{
int k,b;
data(){k=1,b=0;}
data(int k,int b):k(k),b(b){}
int cal(int x){return (k*x+b)%p;}
};
data operator+(data a,data b){
data ret;
ret.k=a.k*b.k%p;
ret.b=(a.b*b.k%p+b.b)%p;
return ret;
}
void exgcd(int a,int b,int &x,int &y){
if(!b)x=1,y=0;
else{
exgcd(b,a%b,y,x);
y-=a/b*x;
}
}
struct linkcuttree{
int ch[maxn+10][2],fa[maxn+10],sfa[maxn+10];
data val[maxn+10],sum[maxn+10];
int rc(int fa,int o){return ch[fa][1]==o;}
int top(int o){return ch[fa[o]][0]!=o&&ch[fa[o]][1]!=o;}
void pushup(int o){sum[o]=sum[ch[o][0]]+val[o]+sum[ch[o][1]];}
int vis[maxn+10],ins[maxn+10];
void dfs(int u){
ins[u]=vis[u]=1;
int v=fa[u];
if(ins[v]){
fa[u]=0;
sfa[u]=v;
}
if(!vis[v])dfs(v);
ins[u]=0;
}
void init(int n){
for(int i=1;i<=n;i++){
int k,b;
scanf("%d%d%d",&k,&fa[i],&b);
val[i]=sum[i]=data(k,b);
}
for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
}
void rot(int o){
int f=fa[o],r=rc(f,o);
if(!top(f))fa[ch[fa[f]][rc(fa[f],f)]=o]=fa[f];
else fa[o]=fa[f];
fa[ch[f][r]=ch[o][r^1]]=f;
fa[ch[o][r^1]=f]=o;
pushup(f);
pushup(o);
}
void splay(int o){
while(!top(o)){
if(!top(fa[o])&&rc(fa[fa[o]],fa[o])==rc(fa[o],o))rot(fa[o]);
rot(o);
}
}
void access(int u){
int v=0;
while(u){
splay(u);
ch[u][1]=v;
pushup(u);
v=u;
u=fa[u];
}
}
int findroot(int u){
access(u);
splay(u);
int ro=u;
while(ch[ro][0])ro=ch[ro][0];
splay(ro);
return ro;
}
int query(int u){
access(u);
splay(u);
data v1=sum[u];
int ro=findroot(u),f=sfa[ro];
access(f);
splay(f);
data v2=sum[f];
if(v2.k==1)return v2.b?-1:-2;
if(v2.k==0)return v1.cal(v2.b);
int x,y;
exgcd(v2.k-1,p,x,y);
return v1.cal((p-x)%p*v2.b%p);
}
void cut(int u){
access(u);
splay(u);
fa[ch[u][0]]=0;
ch[u][0]=0;
pushup(u);
}
void join(int u,int f){
access(u);
splay(u);
fa[u]=f;
}
int oncirclr(int u,int ro){
int f=sfa[ro];
if(u==f)return 1;
access(f);
splay(f);
splay(u);
return !top(f);
}
void update(int u,int f,int k,int b){
access(u);
splay(u);
val[u]=data(k,b);
pushup(u);
int ro=findroot(u);
if(u==ro){
int rf=findroot(f);
if(rf==ro)sfa[u]=f;
else{
sfa[u]=0;
join(u,f);
}
}else{
if(oncirclr(u,ro)){
cut(u);
join(ro,sfa[ro]);
sfa[ro]=0;
int rf=findroot(f);
if(rf==u)sfa[u]=f;
else join(u,f);
}else{
cut(u);
int rf=findroot(f);
if(rf==u)sfa[u]=f;
else join(u,f);
}
}
}
}lct;
int main(){
int n;
scanf("%d",&n);
lct.init(n);
int q;
scanf("%d",&q);
char e[2];
int x,k,b,f;
while(q--){
scanf("%s%d",e,&x);
if(e[0]=='A')printf("%d\n",lct.query(x));
else{
scanf("%d%d%d",&k,&f,&b);
lct.update(x,f,k,b);
}
}
return 0;
}