[NOI2023] 桂花树 题解

哇,真的是神仙中的神仙题,很可怕很可怕。


首先显然的性质是答案与树形态无关,因为其它点肯定在原先的边上或点下,而显然此时答案与树的形态本身是没有关系的。

我们考虑对于最后的 \(k\) 个点做状压 \(dp\)。设 \(f_{i,s}\) 表示枚举到第 \(i\) 个数,最后 \(k\) 个点的祖先里是否有比自己还大的点的状态。则有:

\[f_{i,s}\to f_{i+1,2(s-2^j)} (j\in s) \]

\[f_{i,s}\to f_{i+1,2s},f_{i,s}\to f_{i+1,2s+1} \]

时间复杂度 \(O(Tmk2^{k-1})\)。(\(-1\) 是因为在枚举 \(j\) 的时候可以使用 \(lowbit\) 优化掉一半的常数)

#include<bits/stdc++.h>
using namespace std;
const int M=(1<<10),p=1e9+7;
int t,n,m,k,mx,f[M],g[M];
void solve(){
	cin>>n>>m>>k,mx=(1<<k),f[0]=1;
	for(int i=1;i<mx;i++) f[i]=0;
	for(int i=1,x;i<n;i++) cin>>x;
	for(int i=0;i<m;i++){
		for(int s=0;s<mx;s++) g[s]=f[s],f[s]=0;
		for(int s=0,c=n+i;s<mx;s++,c=n+i) if(g[s]){
			for(int t=s,j,nx;t;t^=j){
				j=t&-t,nx=(s^j)<<1,c++;
				if(nx<mx) f[nx]=(f[nx]+g[s])%p;
			}int nx=s<<1;
			if(nx>=mx) continue;
			f[nx]=(f[nx]+1ll*g[s]*(c*2-1))%p;
			f[nx+1]=(f[nx+1]+1ll*g[s]*(c-1))%p;
		}
	}cout<<f[0]<<"\n";
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t>>t;
	while(t--) solve();
	return 0;
}
posted @ 2025-03-26 18:03  长安一片月_22  阅读(50)  评论(0)    收藏  举报