[九省联考2018]秘密袭击

[Luogu4365] [LOJ2473]

给定一棵树,求其所有联通块的权值第k大的和

树形DP统计\(f[u][i]\),表示\(u\)作为第\(i\)大的方案数 , 答案就是\(\sum f[u][K]\)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

const int N=1705;
const int M=3405;
const int mod=64123;

struct Edge{
    int v,w,nxt;
}e[M];
int first[N],Ecnt=0;
inline void Add_edge(int u,int v,int w=0){
    e[++Ecnt]=(Edge){v,w,first[u]};
    first[u]=Ecnt;
}

int f[N][N],size[N],tmp[N],a[N],b[N];
int n,K,W,ans;

inline int add(int x,int y){x+=y;return x>mod?x-mod:x;}
inline int mul(LL x,int y){x*=y;return x>mod?x%mod:x;}

inline void dfs(int u,int pre){
    f[u][b[u]]=1,size[u]=b[u];//第0大:无影响
    for(int i=first[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==pre) continue;
        dfs(v,u);
        for(int j=0;j<=size[u]&&j<=K;j++){
            for(int k=0;k<=size[v]&&k<=K;k++)
                tmp[j+k]=add(tmp[j+k],mul(f[u][j],f[v][k]));
        }
        size[u]+=size[v];
        for(int j=0;j<=size[u];j++){
            f[u][j]=add(f[u][j],tmp[j]);
            tmp[j]=0;
        }
    }
}

int main(){
    n=read(),K=read(),W=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n-1;i++){
        int x=read(),y=read();
        Add_edge(x,y);
        Add_edge(y,x);
    }
    for(int i=1;i<=n;i++){
        int sum=0;
        for(int j=1;j<=n;j++){
            if(a[j]>a[i]||(a[j]==a[i]&&j>=i)) b[j]=1,sum++;//只算权值>=它的
            else b[j]=0;
        }
        if(sum<K) continue;
        memset(f,0,sizeof f);
        dfs(i,0);
        ans=add(ans,mul(a[i],f[i][K]));//i点作为第K大算贡献
    }
    printf("%d\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/lizehon/p/10597007.html
今日推荐