[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;
}

浙公网安备 33010602011771号