首页 > 其他分享 >P10660 BZOJ2759 一个动态树好题 题解

P10660 BZOJ2759 一个动态树好题 题解

时间:2024-08-18 20:08:17浏览次数:6  
标签:rt ch P10660 int 题解 void nd 树好题 inline

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

对于任意 \(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;
}

标签:rt,ch,P10660,int,题解,void,nd,树好题,inline
From: https://www.cnblogs.com/11-twentythree/p/18365622

相关文章

  • 洛谷P1001题解
    洛谷P1001题解友情提示:“题目传送门”被贴在了题目编号上,请自行点击查看!主要知识点C/C++语言框架基本数据类型的定义与使用cin/cout或scanf()/prinf()的使用代码一小步,OI一大步(bushi)AC代码#include<bits/stdc++.h>typedeflonglongll; //“十年OI一场空,不开long......
  • 题解:CF1630F Making It Bipartite
    题意图上有\(n\)个点,且具有点权,点权保证互不相同,若两个点点权有倍数关系,则两点之间有一边,问你最少删去多少个点能使图变为二分图。思路因为如果\(a\)是\(c\)的倍数且\(c\)是\(b\)的个数,所以\(a\)是\(c\)的倍数。由此可以看出,若\(a\)与\(b\)相连且\(b\)与......
  • 题解:CF1034B Little C Loves 3 II
    思路看到这道题时,第一思路就是网络流,结果一看数据\(10^{9}\)直接转向找规律。主要思路:神秘特判。首先,下面的结论基于\(n\lem\)。Case1.当\(n=1\)时,易得的是我们可以以\(6\)为循环节构造。Case2.当\(n=2\)时,我们可以构造出\(4a,5a,6a\)的形式。易得,通过裴蜀......
  • ABC 367 G 题解
    ABC367G神奇题目场上想到了引入多元生成函数之后就嗝屁了。定义两个多项式的运算\(A(z)*B(z)=\sum_{i}\sum_{j}z^{i\oplusj}a_ib_j\),也就是异或卷积。定义两个二元生成函数\(A(x,y)*B(x,y)=\sum_{i,p}\sum_{j,q}x^{i\oplusj}y^{p+q}a_{i,p}b_{j,q}\)我们仍然选用\(\prod......
  • 题解:AT_abc367_d [ABC367D] Pedometer
    首先肯定要单层循环枚举元素,然后想方法求出一个元素的所有答案。一开始我写了一个二分找\(m\)倍数的方法,发现\(m\)小的时候还不如暴力。于是联想到之前做过的一道题,可以借助于取模的前缀和数组。对于当前元素\(i\),如果一个元素之前的前缀和与\(i\)之前的前缀和对\(m\)......
  • Atcoder [ABC367F] Rearrange Query 题解
    简要题意给定两个长度为\(N\)的序列\(A\)和\(B\)。有\(Q\)个查询,每个查询给定\(l,r,L,R\),其中\(l\leqr,L\leqR\),要求判断\(A\)的第\(l\)项到第\(r\)项构成的集合与\(B\)的第\(L\)项到第\(R\)项构成的集合是否相等。题解显然两个相等的集合所有元素......
  • Atcoder [ABC367C] Enumerate Sequences 题解
    简要题意给定\(n,k\)和\(R_i\),你需要输出所有满足下列条件的整数序列:长度为\(n\)。第\(i\)个元素的范围为\([1,R_i]\)。一个序列的所有元素的总和为\(k\)的倍数。输出请按照按照从左至右按位从小到大的顺序输出。题解注意到数据范围很小,我们可以直接爆搜,这里用......
  • 【动态规划、dp】[CSP-J 2022] 上升点列 题解
    题目描述在一个二维平面内,给定nnn个整数点(xi......
  • 题解:AT_abc367_e [ABC367E] Permute K times
    题意给你一个长度为\(N\)的序列\(X\),其中每个元素都介于\(1\)和\(N\)之间(含),以及一个长度为\(N\)的序列\(A\)。打印在\(A\)上执行以下操作\(K\)次的结果。将\(A_i\)替换为\(A_{X_i}\)。每个操作同时进行。思路元素\(i\)经过\(k\)次变化后的值就是元素......
  • 对树链剖分的爱 题解
    前言题目链接:洛谷。题意简述给出一棵\(n\)个节点以\(1\)为根的有根树。对于第\(2\leqi\leqn\)个节点,其父亲\(f_i\)在\([l_i,r_i]\)中均匀随机。每个树的边有边权,初始为\(0\)。现在有\(m\)次操作,第\(i\)次操作表示将\((u_i,v_i)\)的路径上所有的边的权值统......