• [BZOJ5314][JSOI2018]潜入行动


    [BZOJ5314][JSOI2018]潜入行动

    有一说一,这题在洛谷上能评到黑题??

    但是其实还可以了,锻炼细节能力和分析能力吧


    题目大意:

    有n个点,有k个监视器,每个点至多放一个,被放在点上的监视器可以监控到与他相连的所有点但不包括他自己,求覆盖到整棵树并且用完k个监视器的方案数


    状态应该挺好想的:

    (dp[i][j][0/1][0/1])表示在根为(i)的子树中放了(j)个监视器,(i)是否放了,(i)是否被监视到,0表示没有。

    然后是比较麻烦和细节的转移

    注意:先把(dp[i])拷贝出来并清空,然后再dp

    讲下转移的过程

    对于树u,枚举儿子v,在v之前放了多少个监视器j,在v中放了多少个监视器t

    则由(dp[u][j]×dp[v][t])就可以转移到(dp[u][j+t])

    具体一些:

    (f为拷贝出来的dp[u])

    第一种情况:u上不放,u不被监视到

    [dp[u][j+t][0][0]+=f[j][0][0]×dp[v][j][0][1] ]

    分析:v之前u必须也不放,不被监视到,v上不能放,但是要保证u的子树都要被监视到,所以v的第四维为1,根据乘法原理计数即可

    第二种情况:u上放,u不被监视到

    分析:在v之前u上放了,由于u上放了,所以会让v一定被监视到,所以(dp[v][t][0])的第四维取0/1均可(因为在这之后v始终会被u监视)

    [dp[u][j+t][1][0]+=f[u][j][1][0]×(dp[v][t][0][0]+dp[v][t][0][1]) ]

    第三种情况:u上不放,u被监视到了

    分析:由于u不放所以v必须已经被监视到

    然后两种子情况:

    1、在v之前u已经被监视,此时v上放不放监视器都不影响,所以都取

    [dp[u][j+t][0][1]+=f[j][0][1]×(dp[v][t][0][1]+dp[v][t][1][1]) ]

    2、在v之前u未被监视到,由于v上放了监视器所以u被监视到了

    [dp[u][j+t][0][1]+=f[j][0][0]×dp[v][t][1][1] ]

    都加起来就行

    第四种情况:u上放了,u被监视到

    分析:因为u上放了,所以v被不被监视到都可以取,因为v在这之后会被u监视到

    依然是两种子情况:

    1、在v之前u已经被监视到了:此时v上放不放监视器都行

    (那就是v上放不放监视器,v被不被监视到都行,共4种

    [dp[u][j+t][1][1]+=f[j][1][1]×(dp[v][t][0][0]+dp[v][t][0][1]+dp[v][t][1][0]+dp[v][t][1][1]) ]

    2、在v之前u尚未被监视到:此时v上必须放监视器

    [dp[u][j+t][1][1]+=f[j][1][0]×(dp[v][t][1][0]+dp[v][t][1][1]) ]

    作为个强迫症,我把第一个dp中(f[j][1][1]×(dp[v][t][1][0]+dp[v][t][1][1]))的部分也放在下面来了(解释下代码和分析的不一样之处

    最后输出答案的时候,要把(dp[1][k][0][1])(dp[1][k][1][1])加起来,因为1节点放不放都行,只要监视到即可。


    #include<bits/stdc++.h>
    #define rep(i,x,y) for (int i=x;i<=y;i++)
    #define res(i,x,y) for (int i=x;i>=y;i--)
    using namespace std;
    typedef long long ll;
    const int maxn=100010,mod=1000000007;
    int n,k;
    struct Edge{int v,nex;}edge[maxn<<1];
    int cnt=0,head[maxn];
    int dp[maxn][105][2][2];
    int siz[maxn];
    inline void addEdge(int u,int v){
        edge[++cnt]=(Edge){v,head[u]};head[u]=cnt;
    }
    void add(int &x,int y){
        x+=y;
        if (x>mod)x-=mod;
    }
    int f[105][2][2];
    void dfs(int u,int fa){
        dp[u][0][0][0]=dp[u][1][1][0]=1;siz[u]=1;
        //printf("u=%d
    ",u);
        for (int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if (v==fa) continue;
            dfs(v,u);
            memcpy(f,dp[u],sizeof(f));
            rep(j,0,k) rep(x1,0,1) rep(x2,0,1) dp[u][j][x1][x2]=0;
            rep(j,0,min(siz[u],k))
                rep(t,0,min(siz[v],k)){
                    if (j+t>k) continue;
                    add(dp[u][j+t][0][0],(ll)f[j][0][0]*dp[v][t][0][1]%mod);
                    add(dp[u][j+t][1][0],(ll)f[j][1][0]*(dp[v][t][0][1]+dp[v][t][0][0])%mod);
                    add(dp[u][j+t][0][1],(ll)f[j][0][1]*(dp[v][t][1][1]+dp[v][t][0][1])%mod);
                    add(dp[u][j+t][0][1],(ll)f[j][0][0]*dp[v][t][1][1]%mod);
                    add(dp[u][j+t][1][1],(ll)(f[j][1][0]+f[j][1][1])*(dp[v][t][1][0]+dp[v][t][1][1])%mod);
                    add(dp[u][j+t][1][1],(ll)f[j][1][1]*(dp[v][t][0][0]+dp[v][t][0][1])%mod);
                }
            siz[u]+=siz[v];
        }
    }
    int read(){
        int x=0;char ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        while (isdigit(ch)) x=x*10+ch-48,ch=getchar();
        return x;
    }
    int main(){
        n=read();k=read();
        memset(dp,0,sizeof(dp));
        rep(i,1,n-1){
            int u=read(),v=read();
            addEdge(u,v);addEdge(v,u);
        }
        dfs(1,0);
        add(dp[1][k][0][1],dp[1][k][1][1]);
        printf("%lld
    ",dp[1][k][0][1]);
        return 0;
    }
    
  • 相关阅读:
    速达5000出现计算成本数据溢出的问题
    速达软件 移动端 App 功能说明
    无法打开物理文件 操作系统错误 5: 5(拒绝访问。) 问题解决
    速达软件开发版使用技巧-销售开单单据打印格式设计
    开发版速达软件如何进行有效授权
    速达软件 移动端 App 下载试用
    速达软件二次开发-试用版本下载,欢迎合作
    速达软件开发版使用技巧-每页固定行样式报表设计
    Cause: org.postgresql.util.PSQLException: 栏位索引超过许可范围:13,栏位数:12
    springboot 开启事务回滚
  • 原文地址:https://www.cnblogs.com/ugly-CYW-lyr-ddd/p/11802493.html
Copyright © 2020-2023  润新知