P10660 BZOJ2759 一个动态树好题 题解

从题目名字看出此题需要用动态树解决

对于任意 \(i\),都有唯一的 \(p_i\) 与之对应,由 \(p_i\)\(i\) 连边,\(n\) 种关系显然构成一基环树森林。对于环上的节点,一个点可以自己表示自己,所以可以直接解出该点的权值,其他点从环上的点直接推出即可。

考虑如何动态维护这个过程,一个点上对应的线性变换显然可以用矩阵刻画(其实只是不想动脑子),对于一棵基环树,我们把它多出来那一条边,和根节点的答案都存在根节点上(这样的话换根就要慎重一些了),询问时直接链乘积求出系数即可。

对于任意 \(\text{Link}(x,y)\) 操作,若 \(x,y\) 不连通则直接连边(注意此处 \(\text{MakeRoot}(x)\) 也没有影响,因为能连出边的点一定不属于一棵基环树)。否则算出 \(x\) 的答案,并把答案和这条边存在 \(x\) 上。

修改的时候,设此处原来的边为 \((y,x)\),现换为 \((z,x)\)。那么我们先 \(\text{Cut}(x,y)\)(此处根节点需要特判,因为它连向父亲的边没有真实连上),然后 \(\text{Link}(x,z)\),最后如果 \(x\) 不是根节点的话,要把 \(x\) 所属的基环树的那条虚边再 \(\text{Link}\) 一遍(因为修改后环可能被拆了,此时应该将它真实连上以保证正确性)。

最后关于无解和多解。可以证明,如果根无解,那么整棵树所有节点都无解,多解也有一样的性质。

大常数代码
#include<bits/stdc++.h>
using namespace std;
inline void rd(){}
template<typename T,typename ...U>
inline void rd(T &x,U &...args){
	char ch=getchar();
	T f=1;x=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	x*=f;rd(args...);
}
const int N=3e4+5,mod=10007;
struct Matrix{
	int m[2][2];
	Matrix(){memset(m,0,sizeof m);}
	Matrix(int _x){memset(m,0,sizeof m);m[0][0]=m[1][1]=_x;}
	Matrix friend operator+(Matrix a,Matrix b){
		Matrix c;
		for(int i=0;i<=1;i++)
			for(int j=0;j<=1;j++)c.m[i][j]=(a.m[i][j]+b.m[i][j])%mod;
		return c;
	}
	Matrix friend operator*(Matrix a,Matrix b){
		Matrix c;
		for(int i=0;i<=1;i++)
			for(int j=0;j<=1;j++)
				for(int k=0;k<=1;k++)(c.m[i][j]+=a.m[i][k]*b.m[k][j])%=mod;
		return c;
	}
};
inline int KSM(int x,int n){
	int ans=1;
	while(n){
		if(n&1)ans=ans*x%mod;
		x=x*x%mod;
		n>>=1;
	}
	return ans;
}
int n,q;
namespace LCT{
	int f[N],ch[N][2],tag[N];
	Matrix m[N],prd[N];
	struct{int x,y,ans;}nd[N];
	#define ls ch[p][0]
	#define rs ch[p][1]
	inline void PushUp(int p){prd[p]=prd[ls]*m[p]*prd[rs];}
	inline void Rev(int p){
		tag[p]^=1;swap(ls,rs);
		PushUp(p);
	}
	inline void PushDown(int p){
		if(!tag[p])return ;
		Rev(ls),Rev(rs);tag[p]=0;
	}
	inline int Get(int p){return ch[f[p]][1]==p;}
	inline int IsRoot(int p){return ch[f[p]][1]!=p&&ch[f[p]][0]!=p;}
	void Update(int p){
		if(!IsRoot(p))Update(f[p]);
		PushDown(p);
	}
	inline void Rotate(int x){
		int y=f[x],z=f[y],k=Get(x);
		if(!IsRoot(y))ch[z][Get(y)]=x;
		ch[y][k]=ch[x][!k],ch[x][!k]=y;
		f[y]=x,f[x]=z,f[ch[y][k]]=y;
		PushUp(y),PushUp(x);
	}
	inline void Splay(int x){
		Update(x);
		for(int fa=f[x];!IsRoot(x);Rotate(x),fa=f[x])
			if(!IsRoot(fa))Rotate(Get(fa)==Get(x)?fa:x);
	}
	inline int Access(int x){
		int p;
		for(p=0;x;p=x,x=f[x])
			Splay(x),ch[x][1]=p,PushUp(x);
		return p;
	}
	inline void MakeRoot(int x){
		x=Access(x);Rev(x);
	}
	inline int FindRoot(int p){
		p=Access(p);
		while(ls)p=ls,PushDown(p);
		return Splay(p),p;
	}
	inline void Link(int x,int y){
		if(FindRoot(x)==FindRoot(y)){
			MakeRoot(x);Access(y);
			Splay(x);Matrix mm=prd[ch[x][1]]*m[x];
			nd[x].x=x,nd[x].y=y;
			int k=(mm.m[0][0]+mod-1)%mod,b=mod-mm.m[1][0];
			if(k==0&&b==0)nd[x].ans=-2;
			else if(k==0)nd[x].ans=-1;
			else nd[x].ans=1ll*b*KSM(k,mod-2)%mod;
		}else{
			MakeRoot(x);
			Splay(x);f[x]=y;
		}
	}
	inline void Modify(int x,int k,int y,int b){
		int rt=FindRoot(x);
		Splay(x);
		m[x].m[0][0]=k,m[x].m[1][0]=b;
		PushUp(x);
		if(rt!=x){
			Access(x);Splay(x);
			f[ch[x][0]]=0,ch[x][0]=0;
			Link(x,y);
			Link(nd[rt].x,nd[rt].y);
		}
		else Link(x,y);
	}
	inline int Query(int p){
		int rt=FindRoot(p);Access(p);
		Splay(rt);
		int k=prd[ch[rt][1]].m[0][0],b=prd[ch[rt][1]].m[1][0];
		if(nd[rt].ans==-1)return -1;
		if(nd[rt].ans==-2)return -2;
		return (1ll*k*nd[rt].ans%mod+b)%mod;
	}
	#undef ls
	#undef rs 
}
int p[N];
using namespace LCT;
signed main(){
	rd(n);
	prd[0]=m[0]=Matrix(1);
	for(int i=1;i<=n;i++){
		int k,b;rd(k,p[i],b);
		m[i].m[0][0]=k;
		m[i].m[1][0]=b;
		m[i].m[1][1]=1;
	}
	for(int i=1;i<=n;i++)Link(i,p[i]);
	rd(q);
	while(q--){
		char s[3];int a,x,y,z;
		scanf("%s",s);
		if(s[0]=='A'){
			rd(a);
			printf("%d\n",Query(a));
		}else if(s[0]=='C'){
			rd(a,x,y,z);
			Modify(a,x,y,z);
		}
	}
	return 0;
}
posted @ 2024-08-18 19:52  KIreteria  阅读(14)  评论(0编辑  收藏  举报