[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\n",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\n",dp[1][k][0][1]);
    return 0;
}
posted @ 2019-11-05 23:24  CYW_lyr  阅读(124)  评论(0编辑  收藏  举报