把每个i和p[i]连边
实际上是要维护一个基环森林,一个环的方程就可以直接解
对于一条路径上的方程,根据方程的线性性,我们可以合并这条路径上的方程得到一个关于路径两个端点的方程
用LCT维护
环就拆一条边,在这条边的起点记录一个special信息,始终把这样的点设为根,然后求解就先用exgcd求出根节点的解然后用LCT求出需要求的点的解
修改都是单点修改,需要讨论下
先看是不是根,如果是根就cut掉special,然后结束
然后看是否在环上,在环上则将根的special变成fa,不然就cut掉父亲
然后讨论新的父亲是否在这个点的子树中,在就把special设为新的父亲
具体看代码吧
Code:
#include<bits/stdc++.h>
#define mod 10007
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=3e4+5;
int inv[N];
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
struct equa{
int k,b;
equa(){}
equa(int _k,int _b):k(_k),b(_b){}
friend inline equa operator + (const equa &x,const equa &y){return equa(mul(x.k,y.k),add(mul(x.b,y.k),y.b));}
}w[N];
namespace LCT{
int ls[N],rs[N],fa[N],sp[N];
equa e[N];
inline void pushup(int x){
e[x]=w[x];
if(ls[x]) e[x]=e[ls[x]]+e[x];
if(rs[x]) e[x]=e[x]+e[rs[x]];
}
inline int isrs(int x){return rs[fa[x]]==x;}
inline bool isroot(int x){
if(!fa[x]) return 1;
return ls[fa[x]]!=x && rs[fa[x]]!=x;
}
inline void rotate(int x){
int y=fa[x],z=fa[y],b=(ls[y]==x?rs[x]:ls[x]);
if(z && !isroot(y)) (ls[z]==y?ls[z]:rs[z])=x;
fa[x]=z,fa[y]=x;b?fa[b]=y:0;
if(ls[y]==x) rs[x]=y,ls[y]=b;
else ls[x]=y,rs[y]=b;
pushup(y);pushup(x);
}
inline void splay(int x){
while(!isroot(x)){
if(!isroot(fa[x])){
if(isrs(fa[x])==isrs(x)) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
inline void access(int x){for(int y=0;x;y=x,x=fa[x]){splay(x);rs[x]=y;pushup(x);}}
inline void dox(int x){access(x);splay(x);}
inline int findroot(int x){
dox(x);
while(ls[x]) x=ls[x];
splay(x);return x;
}
inline void cut(int x){dox(x);ls[x]=fa[ls[x]]=0;pushup(x);}
inline bool loop(int x,int y){dox(sp[y]);splay(x);return x==sp[y]||(!isroot(sp[y]));}
inline void modify(int x,int k,int p,int b){
dox(x);w[x]=equa(k,b);pushup(x);
int rt=findroot(x);
if(rt==x){
if(findroot(p)==x) sp[x]=p;
else sp[x]=0,fa[x]=p;
}
else{
if(loop(x,rt)){cut(x);splay(rt);fa[rt]=sp[rt];sp[rt]=0;}
else cut(x);
if(findroot(p)==x) sp[x]=p;
else sp[x]=0,fa[x]=p;
}
}
inline void query(int x){
equa lans,rans;
dox(x);lans=e[x];
int rt=findroot(x);
dox(sp[rt]);rans=e[sp[rt]];
if(rans.k==1){
if(rans.b==0) puts("-2");
else puts("-1");
}
else{
int val=mul(inv[dec(1,rans.k)],rans.b);
cout<<add(mul(lans.k,val),lans.b)<<"\n";
}
}
}
using namespace LCT;
int ff[N],id[N],sign=0;
void findloop(int v){
id[v]=sign;
fa[v]=ff[v];
if(id[ff[v]]==sign){fa[v]=0;sp[v]=ff[v];return;}
findloop(ff[v]);
}
char op[10];
int main(){
inv[1]=1;
for(int i=2;i<=N-2;i++) inv[i]=mul((mod-mod/i),inv[mod%i]);
int n=read();
for(int i=1;i<=n;i++) w[i].k=read(),ff[i]=read(),w[i].b=read();
for(int i=1;i<=n;i++) if(!id[i]) ++sign,findloop(i);
int q=read();
while(q--){
scanf("%s",op);
if(op[0]=='A'){
int x=read();
query(x);
}
else{
int x=read(),k=read(),p=read(),b=read();
modify(x,k,p,b);
}
}
return 0;
}
本文详细解析了BZOJ2759题目,通过构建基环森林并运用LCT算法进行路径方程的求解。针对单点修改操作,文章深入探讨了特殊信息的处理方式,包括根节点的特殊状态改变、环上节点的更新策略及子树内特殊信息的重新分配。代码示例展示了完整的实现过程。

604

被折叠的 条评论
为什么被折叠?



