2021.7.19 义乌模拟赛 T4 D
首先这个\(60\)分的树形dp很好打,直接裸的树上背包合并即可。加了一个每次与子树大小取min的剪枝。
时间复杂度是\(O(nk^2)\)
然后写了一发过了。
然后再考虑这个东西的复杂度。
首先考虑产生\(k^2\)贡献的时候,这个显然是只有\(O(\frac{n}{k})\)次的。
然后如果是小于\(k\)合并到大于\(k\),容易发现对于小于\(k\)子树内的每个节点全程只有一次,所以是每个点均摊\(O(k)\)
如果两颗子树都小于\(k\)那么对于每个这种点的贡献是\(O(siz)\)也就是\(O(k)\)
所以时间复杂度\(O(nk)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 100000
#define M 200
#define mod 1000000007
#define mod2 39989
#define eps (1e-7)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,k,x,y,z,siz[N+5],g[M+5][4],dp[N+5][M+5][4];
struct yyy{int to,z;};
struct ljb{
int head,h[N+5];yyy f[N+5<<1];
I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}
}s;
I void dfs(int x,int last){
int i,j,h;yyy tmp;siz[x]=1;dp[x][1][0]=dp[x][0][2]=1;for(int h=s.h[x];h;h=tmp.z){
tmp=s.f[h];if(tmp.to==last) continue;dfs(tmp.to,x);
for(i=0;i<=k;i++) memcpy(g[i],dp[x][i],sizeof(g[i])),Me(dp[x][i],0);
for(i=min(siz[x],k);~i;i--){
for(j=0;j<=siz[tmp.to]&&j+i<=k;j++){
dp[x][i+j][0]=(dp[x][i+j][0]+1ll*g[i][0]*(dp[tmp.to][j][2]+dp[tmp.to][j][3]))%mod;
dp[x][i+j][1]=(dp[x][i+j][1]+1ll*g[i][1]*(1ll*dp[tmp.to][j][0]+dp[tmp.to][j][1]+dp[tmp.to][j][2]+dp[tmp.to][j][3])+1ll*g[i][0]*(dp[tmp.to][j][1]+dp[tmp.to][j][0]))%mod;
dp[x][i+j][2]=(dp[x][i+j][2]+1ll*g[i][2]*dp[tmp.to][j][3])%mod;
dp[x][i+j][3]=(dp[x][i+j][3]+1ll*g[i][3]*(dp[tmp.to][j][1]+dp[tmp.to][j][3])+1ll*g[i][2]*dp[tmp.to][j][1])%mod;
}//dp[x][i][0]=dp[x][i][2]=0;
}siz[x]+=siz[tmp.to];
}
}
int main(){
freopen("disiti.in","r",stdin);freopen("disiti.out","w",stdout);
re int i;scanf("%d%d",&n,&k);for(i=1;i<n;i++) scanf("%d %d",&x,&y),s.add(x,y),s.add(y,x);dfs(1,0);printf("%d\n",(dp[1][k][1]+dp[1][k][3])%mod);
}