20200516小结

(1)组合数上指标求和大法

\sum_{i=1}^nC(i,a)=C(n+1,a+1)(这个可以画一下杨辉三角,把最上面C(a,a)的移动到C(a+1,a+1),即可证明)

\sum_{i=1}^nC(i,a)*C(n-i,b)=C(n+1,a+b+1)

(左式相当于枚举第a+1个球在n+1个球中的位置,其实就是在n+1个球中选择a+b+1个球)

\sum_{i=0}^{max(a,b)}C(a,i)*C(b,n-i)=C(a+b,n)

(把两堆球放在一起选)

有时候不会推式子,就可以尝试考虑它的组合意义

如:https://blog.csdn.net/hzj1054689699/article/details/85857283

(2)01Trie树维护集合整体+1的操作

从低位到高位建01Trie树,+1操作等价于交换左右子树,然后递归左子树

(3)LCT维护树上连通块支持加点删点操作

LCT可以维护子树信息,在Access中进行虚实变换

加点操作可能会连接O(n)个点,我们可以把每一个连通块顶端的点也纳入该连通块的范围,每次查询的时候就把该点的splay右子树与虚子树的答案合并即可

例题:Nauuo and ODT

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 400005
#define LL long long
LL all;
namespace LCT{
	#define lc ch[x][0]
	#define rc ch[x][1]
	//siz:virtual son's size + real son's size
	//vsz:virtual son's size
	int fa[N],ch[N][2],siz[N],vsz[N];LL sum[N];//sum: sum of virtual son's siz^2
	LL val(int x){return 1ll*siz[x]*siz[x];}
	bool pdc(int x){return ch[fa[x]][1]==x;}
	bool nrt(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
	void pushup(int x){siz[x]=siz[lc]+siz[rc]+1+vsz[x];}
	void rot(int x){
		int y=fa[x],z=fa[y];bool flg=pdc(x);
		if(nrt(y))ch[z][pdc(y)]=x;
		if(ch[y][flg]=ch[x][flg^1])
			fa[ch[y][flg]]=y;
		ch[x][flg^1]=y;
		fa[y]=x;fa[x]=z;
		pushup(y);pushup(x);
	}
	void splay(int x){
		for(;nrt(x);rot(x))
			if(nrt(fa[x]))rot(pdc(x)==pdc(fa[x])?fa[x]:x);
	}
	void Access(int x){
		for(int i=0;x;i=x,x=fa[x]){
			splay(x);
			vsz[x]-=siz[i];vsz[x]+=siz[rc];
			sum[x]-=val(i);sum[x]+=val(rc);
			ch[x][1]=i;
			pushup(x);
		}
	}
	int findroot(int x){
		Access(x);splay(x);
		while(lc)x=lc;
		splay(x);return x;
	}
	void link(int x,int y){
		//remove the contribution of the subtree of x y
		Access(x);all-=sum[x]+val(ch[x][1]);
		int z=findroot(y);all-=val(ch[z][1]);
		//add edge and refresh y
		splay(y);fa[x]=y;vsz[y]+=siz[x];sum[y]+=val(x);pushup(y);//siz[y]+=x;
		//refresh the vsz and sum on the real link
		Access(x);
		splay(z);all+=val(ch[z][1]);
	}
	void cut(int x,int y){
		Access(x);all+=sum[x];
		int z=findroot(x);all-=val(ch[z][1]);
		splay(x);ch[x][0]=fa[ch[x][0]]=0;pushup(x);
		splay(z);all+=val(ch[z][1]);
	}
}//----LCT----
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int fa[N];
void dfs(int u,int ff)
{
	fa[u]=ff;
	for(int p=fir[u];p;p=nxt[p])
		if(to[p]!=ff)dfs(to[p],u);
}
vector<pair<int,int> > tmp[N];
int col[N];bool oft[N];LL ans[N];
int main()
{
	int n,m,i,j,u,v,t;
	n=gi();m=gi();
	for(i=1;i<=n;i++){
		col[i]=gi();
		tmp[col[i]].push_back(make_pair(0,i));
	}
	for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
	dfs(1,n+1);
	for(i=1;i<=m;i++){
		u=gi();v=gi();
		tmp[col[u]].push_back(make_pair(i,u));
		col[u]=v;
		tmp[col[u]].push_back(make_pair(i,u));
	}
	for(i=1;i<=n;i++)LCT::link(i,fa[i]);
	for(i=1;i<=n;i++){
		LL pre=0;
		for(j=0;j<int(tmp[i].size());j++){
			t=tmp[i][j].first;
			u=tmp[i][j].second;
			if(!oft[u])LCT::cut(u,fa[u]);
			else LCT::link(u,fa[u]);
			oft[u]^=1;
			ans[t]+=1ll*n*n-all-pre;
			pre=1ll*n*n-all;
		}
		for(j=0;j<int(tmp[i].size());j++)
			if(oft[u=tmp[i][j].second])
				LCT::link(u,fa[u]),oft[u]=0;
	}
	for(i=1;i<=m;i++)ans[i]+=ans[i-1];
	for(i=0;i<=m;i++)
		printf("%lld\n",ans[i]);
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值