[BZOJ2759][LCT][数学]一个动态树好题

BZOJ2759

把每个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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值