题目大意:给定n个形如xi=ki*x_pi+bi mod p的同余方程组 支持修改操作和求解操作
确实好题 感谢此题作者 顺便吐槽一下作者的Splay不加空节点太蛋疼了0.0
将每个点i的父亲设为pi 我们将会得到一座基环树林 将环上的一条边拆掉,在边的起始节点新开个域special_father记录这条边(P.S:好浪费 但是没办法)
于是我们得到了一座森林 显然可以用LCT来维护 每个节点的权值是个二元组(k,b),记录每个点关于答案的线性关系,合并时左侧代入右侧中
查询时将root的special_father进行Access+Splay操作 然后借助这个环通过EXGCD求出root->special_father的值 然后Access+Splay(x)代入出解
若环上k=1 讨论b 若b=0则无穷多解 否则无解 注意k=0时要加一些处理 正常求EXGCD求不出来
单点修改 有向图 没有标记 爽爆了!
修改x的父节点时需要讨论:
首先切掉原先的父节点
如果x是所在树的根 直接切掉special_father
如果x不是根,切掉x与父节点的联系,然后讨论x是否在环上
设x所在树的根节点为root
若root->special_father所在树的根不为root 则x在环上 将root的special_father变为root的fa节点
否则切断父节点完毕
然后讨论新的父节点f是否在x的子树中
若在,将x的special_father设为f
若不在,将x的fa设为f
好题……RE一晚上……居然忘了对LCT中的任意节点进行修改操作都要先Access+Splay了……居然直接把爸爸换了……RE到死啊
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 30300
#define p 10007
using namespace std;
struct line{
int k,b;
line operator + (const line &y) const
{
line re;
re.k=k*y.k%p;
re.b=(b*y.k+y.b)%p;
return re;
}
int F(int x)
{
return (k*x+b)%p;
}
};
struct abcd{
abcd *ls,*rs,*fa,*sfa;
line num,sum;
abcd(int k,int b);
void Push_Up();
}*null=new abcd(1,0),*tree[M];
abcd :: abcd(int k,int b)
{
ls=rs=fa=sfa=null;
num.k=sum.k=k;
num.b=sum.b=b;
}
void abcd :: Push_Up()
{
sum=ls->sum+num+rs->sum;
}
void Zig(abcd *x)
{
abcd *y=x->fa;
y->ls=x->rs;
x->rs->fa=y;
x->rs=y;
x->fa=y->fa;
if(y==y->fa->ls)
y->fa->ls=x;
else if(y==y->fa->rs)
y->fa->rs=x;
y->fa=x;
y->Push_Up();
}
void Zag(abcd *x)
{
abcd *y=x->fa;
y->rs=x->ls;
x->ls->fa=y;
x->ls=y;
x->fa=y->fa;
if(y==y->fa->ls)
y->fa->ls=x;
else if(y==y->fa->rs)
y->fa->rs=x;
y->fa=x;
y->Push_Up();
}
void Splay(abcd *x)
{
while(x->fa->ls==x||x->fa->rs==x)
{
abcd *y=x->fa,*z=y->fa;
if(x==y->ls)
{
if(y==z->ls)
Zig(y);
Zig(x);
}
else
{
if(y==z->rs)
Zag(y);
Zag(x);
}
}
x->Push_Up();
}
void Access(abcd *x)
{
abcd *y=null;
while(x!=null)
{
Splay(x);
x->rs=y;
x->Push_Up();
y=x;
x=x->fa;
}
}
abcd* Find_Root(abcd *x)
{
abcd *y;
Access(x);
Splay(x);
for(y=x;y->ls!=null;y=y->ls);
return y;
}
int n,m,fa[M],v[M],tot;
pair<int,int>EXGCD(int x,int y)
{
if(!y) return make_pair(1,0);
pair<int,int>temp=EXGCD(y,x%y);
return make_pair(temp.second,temp.first-x/y*temp.second);
}
int Inverse(int x)
{
int temp=EXGCD((x+p)%p,p).first;
return (temp%p+p)%p;
}
void DFS(int x)
{
v[x]=tot;
if(v[fa[x]]==tot)
{
tree[x]->sfa=tree[fa[x]];
return ;
}
tree[x]->fa=tree[fa[x]];
if(!v[fa[x]])
DFS(fa[x]);
}
int Query(abcd *x)
{
abcd *root=Find_Root(x);
Access(root->sfa);
Splay(root->sfa);
int k=root->sfa->sum.k;
int b=root->sfa->sum.b;
if(k==1)
{
if(b==0)
return -2;
else
return -1;
}
int temp=(p-b)*Inverse(k-1)%p;
Access(x);
Splay(x);
return x->sum.F(temp);
}
void Modify(abcd *x,int k,abcd *f,int b)
{
abcd *root=Find_Root(x);
x->num.k=k;
x->num.b=b;
x->Push_Up();
if(x==root)
x->sfa=null;
else
{
Access(x);
Splay(x);
x->ls->fa=null;
x->ls=null;
x->Push_Up();
if(Find_Root(root->sfa)!=root)
{
Access(root);
Splay(root);
root->fa=root->sfa;
root->sfa=null;
}
}
Access(x);
Splay(x);
if(Find_Root(f)==x)
x->sfa=f;
else
x->fa=f;
}
int main()
{
int i,x,k,f,b;
char o[10];
cin>>n;
for(i=1;i<=n;i++)
{
scanf("%d%d%d",&k,&f,&b);
fa[i]=f;
tree[i]=new abcd(k,b);
}
for(i=1;i<=n;i++)
if(!v[i])
++tot,DFS(i);
cin>>m;
for(i=1;i<=m;i++)
{
scanf("%s",o);
if(o[0]=='A')
scanf("%d",&x),printf("%d\n", Query(tree[x]) );
else
scanf("%d%d%d%d",&x,&k,&f,&b),Modify(tree[x],k,tree[f],b);
}
}