BZOJ4557 JLOI2016侦察守卫(树形dp)

  下称放置守卫的点为监控点。设f[i][j]为i子树中深度最大的未被监视点与i的距离不超过j时的最小代价,g[i][j]为i子树中距离i最近的监控点与i的距离不超过j且i子树内点全部被监视时的最小代价。开始觉得这只能设成三维状态对这种二维的纠结了半天要怎么处理子树内有点未被监视但监控点的范围可以延伸到子树外的情况冷静了好长时间终于发现自己果然是个弱智既然子树内有点要被监视在子树外监控这个点的点之后带来的效果就肯定要比子树内的监控点强所以根本不用管子树内的监控点了不写标点发泄一下内心的心态爆炸。

  考虑转移。对于f[i][j],显然有f[i][j]=Σf[son][j-1],f[i][0]=Σg[son][d](g本身就可以看做是f的第二维在负数下的拓展)。对于g[i][j],枚举与i号点最近的监控点在哪棵子树,则有g[i][j]=g[k][j-1]+Σf[son][d-j-1]。g[i][d]特殊处理,如果其不要求被监视则g[i][d]=Σg[son][d],虽然不太符合定义但正确性显然,否则g[i][d]=g[k][d-1]+Σg[son][d]。然后考虑将i号点设置为监控点,则有g[i][0]=a[i]+Σf[son][d-1]。最后对所有f和g取min。

  感觉写的莫名其妙,也莫名其妙就过掉了。要是没法1A的话估计得调一年。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 500010
#define inf 1000000000
#define M 22
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<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int n,d,m,a[N],p[N],f[N][M],g[N][M],t=0;
bool flag[N];
struct data{int to,nxt;
}edge[N<<1];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k,int from)
{
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=from) dfs(edge[i].to,k);
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=from)
    {
        f[k][0]+=g[edge[i].to][d];
        for (int j=1;j<=d;j++)
        f[k][j]+=f[edge[i].to][j-1];
    }
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=from) g[k][0]+=f[edge[i].to][d-1];
    g[k][0]+=a[k];for (int i=1;i<=d;i++) g[k][i]=g[k][0];
    for (int j=1;j<d;j++)
    {
        int tot=0;
        for (int i=p[k];i;i=edge[i].nxt)
        if (edge[i].to!=from) tot+=f[edge[i].to][d-j-1];
        for (int i=p[k];i;i=edge[i].nxt)
        if (edge[i].to!=from) g[k][j]=min(g[k][j],g[edge[i].to][j-1]-f[edge[i].to][d-j-1]+tot);
    }
    int tot=0;
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=from) tot+=g[edge[i].to][d];
    if (flag[k])
    {
        for (int i=p[k];i;i=edge[i].nxt)
        if (edge[i].to!=from) g[k][d]=min(g[k][d],g[edge[i].to][d-1]-g[edge[i].to][d]+tot);
    }
    else g[k][d]=tot;
    for (int i=1;i<=d;i++) g[k][i]=min(g[k][i],g[k][i-1]);
    f[k][0]=min(f[k][0],g[k][d]);
    for (int i=1;i<=d;i++) f[k][i]=min(f[k][i],f[k][i-1]);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4557.in","r",stdin);
    freopen("bzoj4557.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),d=read();
    for (int i=1;i<=n;i++) a[i]=read();
    m=read();
    for (int i=1;i<=m;i++) flag[read()]=1;
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();
        addedge(x,y),addedge(y,x);
    }
    dfs(1,1);
    cout<<g[1][d];
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/9892966.html