Ynoi2016 这是我自己发明的

Description

link

支持换根和给定两个点,求子树中满足权值相同的方案数

Solution

换根操作和遥远的国度一题类似,直接对应到 \(\rm{dfn}\) 序上处理即可

后面的问题和 \(\texttt{SNOI2017 一个简单的询问}\) 类似

仍然使用子树对应在 \(\rm{dfn}\) 序上是一段连续区间,然后容斥得到下式

\[\sum_{x=0} ^{\infty} get(l_1-1,x)\times get(l_2-1,x)+get(r_1-1,x)\times get(r_2-1,x)-get(l_1-1,x)\times get(r_2,x)-get(r_1,x)\times get(l_2-1,x) \]

其中 \(get(l,v)\) 表示在 \([1,l]\) 区间中 \(x\) 出现的个数,此时可以移动指针来统计贡献,可能和莫队类似

推荐卡常数技巧:把没有用的询问都删掉(就是\(l_1,l_2\)\(1\)\(0\) 比关系的时候)

Code


const int N=5e5+10;
int a[N],b[N],m,n,T,bl[N],fa[N][25],dep[N],st[N],ed[N],head[N],cnt,tot,dfn,block,ans[N*5],p[N];
struct node{int nxt,to;}e[N<<1];
inline void add(int u,int v){e[++cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt;return ;}
inline void dfs(int x,int fat){
	dep[x]=dep[fat]+1; st[x]=++dfn; fa[x][0]=fat; p[dfn]=x;
	for(int i=1;(1<<i)<=dep[x];++i) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=head[x];i;i=e[i].nxt) if(e[i].to==fat) continue; else dfs(e[i].to,x);
	ed[x]=dfn;
	return ;
}
struct ask{
	int l,r,fl,id;
	inline void init(int a,int b,int c,int d){l=a,r=b,fl=c,id=d; return ;}
	bool operator <(const ask &x) const
	{
		if(bl[l]!=bl[x.l]) return l<x.l;
		if(bl[l]&1) return r<x.r;
		return r>x.r;
	}
}q[N*16];
int res,l=0,r=0,s1[N],s2[N],opt,rt=1,num;
inline void add1(int x){s1[x]++; res+=s2[x]; return ;}
inline void del1(int x){s1[x]--; res-=s2[x]; return ;}
inline void del2(int x){s2[x]--; res-=s1[x]; return ;}
inline void add2(int x){s2[x]++; res+=s1[x]; return ;}
int t1[10],t2[10],now,sz,x,y;
inline void calc(int x){
	if(x==rt){
		t1[++now]=1,t2[now]=n;
	}
	else if(!(st[x]<=st[rt]&&ed[rt]<=ed[x])) {
		t1[++now]=st[x]; t2[now]=ed[x];
	}else{
		int p=dep[rt]-dep[x]-1,y=rt;
		for(int i=0;i<18;++i) if(p&(1<<i)) y=fa[y][i]; 
		t1[++now]=1; t2[now]=st[y]-1;
		t1[++now]=ed[y]+1; t2[now]=n;
	}return ;
}
#define l1 t1[i]
#define l2 t1[j]
#define r1 t2[i]
#define r2 t2[j]
signed main(){
	n=read(); T=read(); block=sqrt(n);
	for(int i=1;i<=n;++i) bl[i]=(i-1)/block+1,a[i]=read(),b[++m]=a[i];
	sort(b+1,b+m+1); m=unique(b+1,b+m+1)-b-1;
	for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+m+1,a[i])-b;
	for(int i=1,u,v;i<n;++i) u=read(),v=read(),add(u,v),add(v,u);
	dfs(1,0);
	while(T--){
		opt=read();
		if(opt==1) rt=read();
		else{
			x=read(),y=read(); ++num; now=0; calc(x); sz=now; calc(y);
			for(int i=1;i<=sz;++i){
				for(int j=sz+1;j<=now;++j){
					if(l1>r1||l2>r2||l1<1||l2<1||r2>n||l2>n) continue; 
					if(l1>1&&l2>1) q[++tot].fl=1,q[tot].id=num,q[tot].l=l1-1,q[tot].r=l2-1;
					q[++tot].fl=1; q[tot].id=num; q[tot].l=r1; q[tot].r=r2;
					if(l1>1) q[++tot].fl=-1,q[tot].id=num,q[tot].r=r2,q[tot].l=l1-1;
					if(l2>1) q[++tot].fl=-1,q[tot].id=num,q[tot].r=r1,q[tot].l=l2-1;
				}
			}
		} 
	}
	for(int i=1;i<=tot;++i) if(q[i].l>q[i].r) swap(q[i].l,q[i].r);
	sort(q+1,q+tot+1);
	for(int i=1;i<=tot;++i){
		while(l<q[i].l) ++l,add1(a[p[l]]);
		while(l>q[i].l) del1(a[p[l]]),--l;
		while(r<q[i].r) ++r,add2(a[p[r]]);
		while(r>q[i].r) del2(a[p[r]]),--r;
		ans[q[i].id]+=q[i].fl*res;
	} 
	for(int i=1;i<=num;++i) printf("%d\n",ans[i]);
	return 0;
}
posted @ 2020-07-04 21:43  yspm  阅读(164)  评论(1编辑  收藏  举报