• 秘密袭击 [BZOJ5250] [树形DP]


    分析:

    听说正解是FFT+线段树合并,然而我并不会...

    我们来思考其他的方法。

    我们要求的是连通块第k大的和

    对于某一个连通块,对答案的贡献=val(Rank.K)

    我们不好直接算出每个连通块的Rank.K是多少

    但我们可以枚举一个limit for 1->w ,Σ(val(Rank.K)>=lim的连通块的个数)就等于答案

    为什么呢,因为这样一个连通块就被统计了val(Rank.K)次。

    剩下的进行树形DP,设dp[i][j]为以i为根的子树,选出j个权值>=limit的点的方案数。

    那么最后统计答案的时候便是Σ(dp[i][j])(K<=j<=size(i))

    复杂度N^3其实是不对的,但是卡一卡常数还是过得去的

    代码:

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define RG register int
    #define rep(i,a,b)    for(RG i=a;i<=b;++i)
    #define per(i,a,b)    for(RG i=a;i>=b;--i)
    #define ll long long
    #define inf (1<<29)
    #define maxn 2000
    #define add(x,y) e[++cnt].v=y,e[cnt].next=head[x],head[x]=cnt
    using namespace std;
    int n,m,cnt,w;
    int ss[maxn],isn[maxn],head[maxn];
    ll lim,ans;
    ll val[maxn],dp[maxn][maxn],sz[maxn];
    //dp[i][j] 在以i为根的子树,选择了j个权值大于等于lim的点的方案数
    const ll mo=64123;
    struct E{
        int v,next;
    }e[maxn<<1];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    inline int MO(int x,int v){x+=v;return x>=mo?x-mo:x;}
    
    void dfs(int u,int fa)
    {
        sz[u]=(val[u]>=lim)?1:0;
        dp[u][sz[u]]=1;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].v;
            if(v==fa)    continue;
            dfs(v,u);
            per(ii,sz[u],0)
                if(dp[u][ii])
                    per(j,sz[v],0)
                        if(dp[v][j])dp[u][ii+j]=MO(dp[u][ii+j],(dp[u][ii]*dp[v][j])%mo);
            sz[u]+=sz[v];
        }
        rep(i,m,sz[u]) ans=MO(ans,dp[u][i]);
    }
    
    int main()
    {
        n=read(),m=read(),w=read();
        rep(i,1,n) val[i]=read(),ss[val[i]]++;
        for(RG i=1,u,v;i<n;i++)    u=read(),v=read(),add(u,v),add(v,u);
        per(i,w,1) ss[i]+=ss[i+1];
        rep(i,1,w)
        {
            if(ss[i]<m)    break;
            memset(dp,0,sizeof(dp));lim=i;
            dfs(1,0);
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    0316-复利计算更新
    实验一 了解和掌握操作系统
    复利计划1.0
    0916 词法分析(2)
    0916词法分析
    0909 编译原理 第1次上机
    单元测试代码
    单元测试
    实验一:命令解释程序
    《构建之法》现代软件工程第1、2、3章读后感
  • 原文地址:https://www.cnblogs.com/ibilllee/p/8780702.html
Copyright © 2020-2023  润新知