1. 程式人生 > >【BZOJ5314】[JSOI2018]潛入行動(動態規劃)

【BZOJ5314】[JSOI2018]潛入行動(動態規劃)

names pan getchar 集合 main def mat 全局 動態規劃

【BZOJ5314】[JSOI2018]潛入行動(動態規劃)

題面

BZOJ
洛谷

題解

不難想到一個沙雕\(dp\),設\(f[i][j][0/1][0/1]\)表示當前點\(i\),子樹中一共放了\(j\)個,這個點是否放了,這個是否被覆蓋了。
看起來直接合並是\(O(nk^2)\)的QwQ。。。。。
然後我以為是\(O(nk^2)\)的就不會做了嚶嚶嚶。
實際上是\(O(nk)\)的。。。
證明大概是這樣的:
考慮什麽時候會產生\(O(k^2)\)的貢獻,即一個點有兩棵子樹的大小大於\(k\),而這樣子合並次數不會超過\(O(\frac{n}{k})\),所以這部分的復雜度是\(O(nk)\)的。

另外一種情況是一個子樹小於\(k\),經過合並之後變成大於\(k\)的子樹了,顯然對於一個點,如果它的子樹小於\(k\),在某次合並之後它的子樹就會大於\(k\),並且對於每個點而言,只會在他的某個祖先的地方經歷一次這樣子的合並,所以這樣子均攤每個點會產生\(O(k)\)的貢獻。
第三種情況是兩個點的子樹大小都小於\(k\),合並完之後兩者還是小於\(k\)。這個操作理解為每個兩個集合中的點一一對應的產生一次貢獻,那麽盯著某一個特定點考慮,它每次產生的貢獻是合並進來的子樹大小的,因為在這一部分的過程中子樹大小總是小於\(k\),因此每個點產生的貢獻也最多是\(O(k)\)的。
綜上,在三種合並情況中,每種情況產生的貢獻都最多是\(O(nk)\)
的,所以全局的復雜度就是\(O(nk)\)

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
#define MOD 1000000007
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,K,f[MAX][101][2][2],size[MAX];
int tmp[101][2][2];
void dfs(int u,int ff)
{
    size[u]=1;f[u][0][0][0]=1;f[u][1][1][0]=1;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(v==ff)continue;dfs(v,u);
        for(int j=0;j<=size[u]&&j<=K;++j)
            for(int k=0;k<=size[v]&&j+k<=K;++k)
            {
                if(f[u][j][0][0])
                {
                    add(tmp[j+k][0][0],1ll*f[u][j][0][0]*f[v][k][0][1]%MOD);
                    add(tmp[j+k][0][1],1ll*f[u][j][0][0]*f[v][k][1][1]%MOD);
                }
                if(f[u][j][0][1])
                {
                    add(tmp[j+k][0][1],1ll*f[u][j][0][1]*(f[v][k][0][1]+f[v][k][1][1])%MOD);
                }
                if(f[u][j][1][0])
                {
                    add(tmp[j+k][1][0],1ll*f[u][j][1][0]*(f[v][k][0][0]+f[v][k][0][1])%MOD);
                    add(tmp[j+k][1][1],1ll*f[u][j][1][0]*(f[v][k][1][0]+f[v][k][1][1])%MOD);
                }
                if(f[u][j][1][1])
                {
                    int s=0;
                    add(s,f[v][k][0][0]);add(s,f[v][k][0][1]);
                    add(s,f[v][k][1][0]);add(s,f[v][k][1][1]);
                    add(tmp[j+k][1][1],1ll*f[u][j][1][1]*s%MOD);
                }
            }
        size[u]+=size[v];
        for(int j=0;j<=size[u]&&j<=K;++j)
        {
            f[u][j][0][0]=tmp[j][0][0];tmp[j][0][0]=0;
            f[u][j][0][1]=tmp[j][0][1];tmp[j][0][1]=0;
            f[u][j][1][0]=tmp[j][1][0];tmp[j][1][0]=0;
            f[u][j][1][1]=tmp[j][1][1];tmp[j][1][1]=0;
        }
    }
}
int main()
{
    n=read();K=read();
    for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
    dfs(1,0);
    int ans=(f[1][K][0][1]+f[1][K][1][1])%MOD;
    printf("%d\n",ans);
    return 0;
}

【BZOJ5314】[JSOI2018]潛入行動(動態規劃)