WXHRound#14被虐记

本文介绍了无标号有根树的计数问题,探讨了O(n^2log n)的解决方案,并提及了母函数的O(nlog^2n)方法。重点讲解了如何利用LCT(线性链状结构)数据结构在O(nlog^2n)的时间复杂度内解决此类问题,包括LCT的维护技巧,如虚子树答案的记录和翻转链的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

T2:无标号有根仙人掌计数,不会

倒是搞懂了O(n^2log n)无标号无根树计数

先考虑无标号有根树的计数

记dp[k]为当我dp到i时用1~i大小的树可以凑出k的方案数

则每次就拿dp[i]去更新dp[k],dp[k]+=[t=1...k/i]C(t,t+dp[i]-1)*dp[k-i*t]

解释:大小为i的树有dp[i]种,相当于求每个元素∈[1,dp[i]]的长为t的非降序列个数,即C(t,t+dp[i]-1)


听说还有母函数O(nlog^2n)的,不会

(这个推广一下就可以求有度数限制的树的个数


#include<bits/stdc++.h>
#define maxn 3010
using namespace std;
int dp[maxn],n,mod,f[maxn];
int qpow(int a,int b){
	int ans=1,tmp=a;
	for(;b;b>>=1,tmp=1ll*tmp*tmp%mod)
		if(b&1)ans=1ll*ans*tmp%mod;
	return ans;
}
int main(){
	scanf("%d%d",&n,&mod),dp[1]=dp[0]=1;
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j)f[j]=dp[j];
		for(int k=i,t=1,z=1;k<n;k+=i,t++){
			z=1ll*z*(dp[i]+t-1)%mod*qpow(t,mod-2)%mod;
			for(int j=k+1;j<=n;++j)
				f[j]=(f[j]+1ll*z*dp[j-k])%mod;
		}
		for(int j=i+1;j<=n;++j)dp[j]=f[j];
	}
	printf("%d",dp[n]);
}

T3:有一个有根树森林,支持Link-Cut,每次输出离根最远的点

出题人用的ETT...然而LCT直接就过了(还跑的比ETT不知道快到哪里去了,可能是我们OJ对LCT友好?

这个如何用LCT维护?每个节点多弄一个val[x]表示距离最高点的距离,map<int,int>记录虚子树们的答案

每次access就改一下map,给儿子一个val加标记

翻转链有点麻烦,因为是val[x]=size-1-val[x],其中有一个取负操作

于是就维护两个值,一个是正的val[x],一个是负的val[x],取负时交换一下就可以了


复杂度O(nlog^2n)

#include<bits/stdc++.h>
#define maxn 400100
using namespace std;
struct data{
	int val,vals;
	data(){}
	data(int val,int vals):val(val),vals(vals){}
	data operator+(const data& d){
		data ret=(*this);
		if(d.val>ret.val)ret.val=d.val,ret.vals=d.vals;
		else if(d.val==ret.val)ret.vals+=d.vals;
		return ret;
	}
};
struct edge{
	int r,nxt;
}e[maxn<<1];
int n,m,head[maxn],esz,ans;
struct MCFXAKIOI2018{
	int ch[maxn][2],fa[maxn],tag[maxn];
	int addv[maxn],sta[maxn],size[maxn],val[maxn];
	data d[maxn],xd[maxn];
	map<int,int> st[maxn];
	void update(int o){
		if(!o)return ;
		d[o]=d[ch[o][0]]+d[ch[o][1]]+(st[o].empty()?data(0,0):data(st[o].rbegin()->first+val[o],st[o].rbegin()->second))+data(val[o],1);
		xd[o]=xd[ch[o][0]]+xd[ch[o][1]]+(st[o].empty()?data(-1000000007,0):data(st[o].rbegin()->first-val[o],st[o].rbegin()->second))+data(-val[o],1);
		size[o]=size[ch[o][0]]+size[ch[o][1]]+1;
	}
	void pushdown(int o){
		if(!o)return ;
		if(tag[o]){
			swap(ch[o][0],ch[o][1]);
			if(ch[o][0])tag[ch[o][0]]^=1,addv[ch[o][0]]*=-1,swap(d[ch[o][0]],xd[ch[o][0]]),val[ch[o][0]]*=-1;
			if(ch[o][1])tag[ch[o][1]]^=1,addv[ch[o][1]]*=-1,swap(d[ch[o][1]],xd[ch[o][1]]),val[ch[o][1]]*=-1;
			tag[o]=0;
		}
		if(addv[o]){
			if(ch[o][0])addv[ch[o][0]]+=addv[o],val[ch[o][0]]+=addv[o],d[ch[o][0]].val+=addv[o],xd[ch[o][0]].val-=addv[o];
			if(ch[o][1])addv[ch[o][1]]+=addv[o],val[ch[o][1]]+=addv[o],d[ch[o][1]].val+=addv[o],xd[ch[o][1]].val-=addv[o];
			addv[o]=0;
		}
	}
	
	bool isroot(int x){
		if(!x||!fa[x])return 1;
		return ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x;
	}
	void rotate(int p){
		if(!p)return ;
		int q=fa[p],y=fa[q],k=(ch[q][1]==p);
		if(!isroot(q))ch[y][ch[y][1]==q]=p;
		fa[ch[q][k]=ch[p][k^1]]=q;
		fa[ch[p][k^1]=q]=p,fa[p]=y;
		update(q),update(p),update(y);
	}
	void print(int x){
		if(!x)return ;
		pushdown(x);
		print(ch[x][0]);
		print(ch[x][1]);
		update(x);
	}
	void splay(int x){
		while(!isroot(x)){
			int y=fa[x];
			pushdown(fa[y]);
			pushdown(y);
			pushdown(x);
	        if(!isroot(y)){
	            if((ch[fa[x]][1]==x)^(ch[fa[y]][1]==y))rotate(x);
	            else rotate(y); 
	        }
	        rotate(x);
		}
		update(x);
	}
	void access(int x){
		for(int y=0;x;y=x,x=fa[x]){
			splay(x),pushdown(x);
			int z=ch[x][1];
			ch[x][1]=0;
			if(z){
				addv[z]-=val[x]+1,val[z]-=val[x]+1;
				d[z].val-=val[x]+1,xd[z].val+=val[x]+1,st[x][d[z].val+1]+=d[z].vals;
			}
			if(y){
				(st[x][d[y].val+1]-=d[y].vals)?0:st[x].erase(d[y].val+1);
				addv[y]+=val[x]+1,val[y]+=val[x]+1,d[y].val+=val[x]+1,xd[y].val-=val[x]+1;
			}
			ch[x][1]=y;
			update(x);
		}
	}
	
	void rever(int x){
		access(x);
		splay(x);
		ans-=d[x].vals;
		tag[x]^=1;
		swap(d[x],xd[x]);
		d[x].val+=size[x]-1;
		xd[x].val-=size[x]-1;
		val[x]=-val[x]+size[x]-1;
		addv[x]+=size[x]-1;
	}
	void link(int u,int v){
		rever(u);
		print(u);
		access(v);
		splay(v);
		ans=ans-d[v].vals;
		fa[u]=v,st[v][d[u].val+1]+=d[u].vals;
		update(v);
		ans=ans+d[v].vals;
	}
	void cut(int u){
		access(u),splay(u);
		ans-=d[u].vals;
		int fu=ch[u][0];
		pushdown(fu);
		while(fu&&ch[fu][1])fu=ch[fu][1],pushdown(fu);
		splay(fu);
		fa[u]=ch[fu][1]=0;
		addv[u]-=val[fu]+1,val[u]-=val[fu]+1;
		d[u].val-=val[fu]+1,xd[u].val+=val[fu]+1;
		update(fu);
		ans+=d[fu].vals+d[u].vals;
	}
}lct;
void addedge(int u,int v){
	e[++esz].r=v;e[esz].nxt=head[u];head[u]=esz;
}
void dfs(int u){
	for(int t=head[u];t;t=e[t].nxt)
		dfs(e[t].r),lct.st[u][lct.d[e[t].r].val+1]+=lct.d[e[t].r].vals;
	lct.update(u);
}
int main(){
//	freopen("darkforest10.in","r",stdin);
//	freopen("out.txt","w",stdout);
	lct.xd[0]=lct.d[0]=data(-1000000007,0);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&lct.fa[i]);
		lct.d[i]=lct.xd[i]=data(0,1);
		addedge(lct.fa[i],i);
	}
	for(int i=1;i<=n;++i)if(!lct.fa[i])dfs(i),ans+=lct.d[i].vals;
	printf("%d\n",ans);
	for(int i=1,op,x,y;i<=m;++i){
		scanf("%d",&op);
		if(op==1)scanf("%d%d",&x,&y),lct.link(x,y);
		else scanf("%d",&x),lct.cut(x);
		printf("%d\n",ans);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值