6687. 【JZOJ 省选模拟】树没了(tree)

题目

Description
在这里插入图片描述

Input

在这里插入图片描述
Output
2操作的个数行,每行代表一个询问的答案。

Sample Input
5 5 1
1 2 3 4 5
1 2
2 3
2 4
1 5

Q 1
M 4 1
Q 1
M 2 1
Q 1

Sample Output
1
2
3

Data Constraint
在这里插入图片描述

思路

考虑将点的颜色放到父亲的边上,再用LCT维护每一种颜色的联通块
总点数是O(n+q)级别的
一个同色联通块,对应树中一个联通块+非这个颜色的根
那么,LCT加边删边要维护影响到的联通块的大小。注意要预处理各个底数的k次幂

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+77,mod=1e9+7;
int n,q,k,x,y,ts,va[N],yjy[N],yjy2[N],fa[N],d[N];
ll ans[N];
char c;
vector<int>e[N];
map<int,int>pos[N];
struct tr
{
	tr *f,*s[2];
	ll sum;
	int si,a;
	bool is;
	void up() 
	{
		si=a+s[0]->si+s[1]->si;
	}
	void ro() 
	{
		tr *y=f;int z=y->s[1]==this;
		(y->s[z]=s[!z]) ->f=y;
		f=y->f;y->is?f->s[y==f->s[1]]=this:0;
		(s[!z]=y) ->f=this;y->up();swap(is,y->is);
	}
	void sp() 
	{
		for(;is;ro() ) 
			if(f->is) 
				(this==f->s[1]) ==(f==f->f->s[1]) ?f->ro() :ro();
		up();
	}
}t[N*4];
void ac(tr *x) 
{
	tr *xx=x,*y=t;
	for(;x!=t;y=x,x=x->f) 
	{
		x->sp();
		x->sum+=va[x->s[1]->si]-va[y->si];
		x->a+=x->s[1]->si-y->si;
		x->s[1]->is=0;
		(x->s[1]=y) ->is=1;
		x->up();
	}
	xx->sp();
}
int power(int x,int y) 
{
	int t=1;
	for(;y;y>>=1,x=(ll) x*x%mod) if(y&1) t=(ll) t*x%mod;
	return t;
}
int get(int x,int c) 
{
	if(pos[c].count(x) ) return pos[c][x];
	pos[c][x]=++ts;
	t[ts].f=t[ts].s[0]=t[ts].s[1]=t;
	t[ts].a=t[ts].si=1;
	t[ts].sum=0;
	return ts;
}
int find_root(tr *x) 
{
	tr *y;
	for(y=x;y->s[0]!=t;) y=y->s[0];
	y->sp();
	return y-t;
}
void add(tr *x,tr *y) 
{
	x->sp();
	y->sp();
	x->f=y;
	x->is=0;
	y->a+=x->si;
	y->sum+=va[x->si];
	y->up();
}
void cut(tr *x,tr *y) 
{
	x->sp();
	y->sp();
	x->f=y->s[1]=t;
	x->is=0;
	y->up();
}
void del(int x,int c) 
{
	int px=get(x,c) ,py=get(fa[x],c) ,rt;
	ac(t+px);
	(ans[c]+=t[px].sum) %=mod;
	rt=find_root(t+px);
	(ans[c]-=va[t[rt].s[1]->si]) %=mod;
	cut(t+px,t+py);
	t[rt].sp();
	(ans[c]+=va[t[rt].s[1]->si]) %=mod;
}
void ins(int x,int c) 
{
	int px=get(x,c) ,py=get(fa[x],c) ,rt;
	ac(t+px);
	(ans[c]-=t[px].sum) %=mod;
	ac(t+py);
	rt=find_root(t+py);
	(ans[c]-=va[t[rt].s[1]->si]) %=mod;
	add(t+px,t+py);
	ac(t+px);
	t[rt].sp();
	(ans[c]+=va[t[rt].s[1]->si]) %=mod;
}
void ch(int x,int c) 
{
	if(c==yjy[x]) return;
	if(yjy[x]) del(x,yjy[x]);
	ins(x,c);
	yjy[x]=c;
}
int main() 
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d%d%d",&n,&q,&k);
	for(int i=1; i<=n; i++) va[i]=power(i,k);
	for(int i=1; i<=n; i++) scanf("%d",&yjy2[i]);
	for(int i=2; i<=n; i++) scanf("%d%d",&x,&y) ,e[x].push_back(y) ,e[y].push_back(x);
	fa[1]=n+1;
	d[1]=1;
	for(int l=0,r=1,x;x=d[++l],l<=r;) 
		for(int i:e[x]) if(i!=fa[x]) 
			fa[i]=x,d[++r]=i;
	for(int i=1; i<=n; i++) ch(i,yjy2[i]);
	int qs=0;
	for(int o=1; o<=q; o++) 
	{
		scanf("\n");
		c=getchar();
		if(c=='M') 
		{
			scanf("%d%d",&x,&y);
			ch(x,y);
		}
		else
		{
			++qs;
			scanf("%d",&x);
			printf("%d\n",(ans[x]%mod+mod) %mod);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值