博主蒟蒻,目前还不会动态dp,所以下面说的是一个并不优秀的暴力,我会补的!
我们考虑按权值从大到小依次点亮每个点,相同权值可以同时点亮,每次点亮后,我们进行一次树形背包。
处理出$f[i][j]$表示i的子树中有j个亮点的方案数,然后就AC了。
有两个小优化,一个是将背包的枚举上限设为min(size[x],K),此处size[x]为子树中点亮的点的的个数。
还有就是我们可以把大于K的dp值都和K合并到一起,因为我们需要的是所有大于等于K的方案数。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #define N 1700 7 #define mod 64123 8 using namespace std; 9 int e=1,head[N]; 10 struct edge{ 11 int v,next; 12 }ed[N<<1]; 13 void add(int u,int v){ 14 ed[e].v=v; 15 ed[e].next=head[u]; 16 head[u]=e++; 17 } 18 int n,m,K,a[N],pp[N]; 19 int f[N][N],g[N],size[N],vis[N],ans,sum,last; 20 bool cmp(int a,int b){return ::a[a]>::a[b];} 21 void dfs(int x,int fa){ 22 for(int i=0;i<=K;i++)f[x][i]=0; 23 size[x]=vis[x];f[x][size[x]]=1; 24 for(int i=head[x];i;i=ed[i].next){ 25 int v=ed[i].v; 26 if(v==fa)continue; 27 dfs(v,x); 28 int up1=min(size[x],K),up2=min(size[v],K); 29 for(int j=min(up1+up2,K);~j;j--)g[j]=0; 30 for(int j=up1;~j;j--) 31 for(int k=up2;~k;k--) 32 (g[min(j+k,K)]+=1ll*f[x][j]*f[v][k]%mod)%=mod; 33 size[x]+=size[v]; 34 up1=min(size[x],K); 35 for(int j=0;j<=up1;j++)f[x][j]=g[j]; 36 } 37 f[x][0]++; 38 (sum+=f[x][K])%=mod; 39 } 40 int main(){ 41 scanf("%d%d%d",&n,&K,&m); 42 for(int i=1;i<=n;i++){ 43 scanf("%d",&a[i]); 44 pp[i]=i; 45 } 46 sort(pp+1,pp+n+1,cmp); 47 for(int i=1,u,v;i<n;i++){ 48 scanf("%d%d",&u,&v); 49 add(u,v);add(v,u); 50 } 51 for(int i=1;i<=n;){ 52 do vis[pp[i]]=1,i++; 53 while(i<=n&&a[pp[i]]==a[pp[i-1]]); 54 if(i<K)continue; 55 sum=0;dfs(1,0); 56 (ans+=1ll*(sum-last+mod)*a[pp[i-1]]%mod)%=mod; 57 last=sum; 58 } 59 printf("%d ",ans); 60 return 0; 61 }