[bzoj 5466]保卫王国

124 篇文章 2 订阅
1 篇文章 0 订阅

Z国有n座城市,n-1条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。
Z国的国防部长小 Z 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:一座城市可以驻扎一支军队,也可以不驻扎军队。由道路直接连接的两座城市中至少要有一座城市驻扎军队。
在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是p_i。小 Z很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 Z 提出了m个要求,每个要求规定了其中两座城市是否驻扎军队。小Z需要针对每个要求逐一给出回答。具体而言,如果国王提出的第j个要求能够满足上述驻扎条件(不需要考虑第 j 个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第j个要求无法满足,则需要输出-1(1≤j≤m)。现在请你来帮助小Z。

这道题ddp做法很显然,这里就不介绍了。
这道题我们可以先考虑一个子问题,如果每次只要求一个点,那么只要换根dp一下就可以了。所以我们发现两个点的话,先求出两个点的最近公共祖先p,枚举状态为id,如果能处理出最小的满足条件的f[p][id](范围为p的子树),还有g[p][id](范围为整棵树-p的子树),相加就可以了。
g数组是很容易处理的,那关键就是怎么处理f。那因为只有两个点,如果我们能处理出一个P[x][y][idx][idy]数组(表示当x的状态为idx,y的状态idy时,范围为y的子树-x的子树)就可以了,但这样空间和时间复杂度都过不去,而我们发现这个数组可以转化成倍增的形式,这样就可以预处理了。
那查询的时候,只要在向上跳的时候枚举中继点的状态,就可以计算答案了。总时间复杂度为 O ( 8 ∗ n l o g n + 4 ∗ q l o g n ) O(8*nlogn+4*qlogn) O(8nlogn+4qlogn),算是比较优秀的。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
struct node
{
    int x,y,next;
}a[200010];int len,last[100010];
inline void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int dep[100010],fa[100010][18];
long long w[100010],f[100010][2],g[100010][2],p[100010][18][2][2];
inline void dfs(int x)
{
    f[x][1]=w[x];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa[x][0])continue;
        dep[y]=dep[x]+1,fa[y][0]=x;
        dfs(y);
        f[x][0]+=f[y][1];
        f[x][1]+=min(f[y][0],f[y][1]);
    }
}
inline void dp(int x)
{
    long long t0=g[x][0],t1=g[x][1]+w[x];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa[x][0])continue;
        t0+=f[y][1],t1+=min(f[y][0],f[y][1]);
    }
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa[x][0])continue;
        g[y][0]=t1-min(f[y][0],f[y][1]),g[y][1]=min(t0-f[y][1],g[y][0]);
        dp(y);
    }
}
inline long long myadd(long long w1,long long w2)
{
    long long mn=min(w1,w2);
    if(mn==-1)return mn;
    else return w1+w2;
}
inline long long mymin(long long w1,long long w2)
{
    if(w1>w2)swap(w1,w2);
    if(w1==-1)return w2;
    else return w1;
}
inline void pre(int x)
{
    for(int i=1;(1<<i)<=dep[x];i++)
    {
        int P=fa[x][i-1];
        fa[x][i]=fa[P][i-1];
        for(int d1=0;d1<=1;d1++)
        {
            for(int d2=0;d2<=1;d2++)
            {
                for(int d3=0;d3<=1;d3++)p[x][i][d1][d2]=mymin(p[x][i][d1][d2],myadd(p[x][i-1][d1][d3],p[P][i-1][d3][d2]));
            }
        }           
    }
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa[x][0])continue;
        p[y][0][1][0]=f[x][0]-f[y][1];
        long long wy=f[x][1]-min(f[y][0],f[y][1]);p[y][0][0][1]=wy,p[y][0][1][1]=wy;
        pre(y);
    }
}
long long nx[2],tx[2],ny[2],ty[2];
inline long long solve(int x,int idx,int y,int idy)
{
    if(dep[x]<dep[y])swap(x,y),swap(idx,idy);
    nx[0]=f[x][0],nx[1]=f[x][1],ny[0]=f[y][0],ny[1]=f[y][1];
    nx[idx^1]=-1,ny[idy^1]=-1;
    for(int i=16;i>=0;i--)
    {
        if(dep[x]-dep[y]>=(1<<i))
        {
            tx[0]=nx[0],tx[1]=nx[1];nx[0]=nx[1]=-1;
            for(int d1=0;d1<=1;d1++)
            {
                for(int d2=0;d2<=1;d2++)nx[d2]=mymin(nx[d2],myadd(tx[d1],p[x][i][d1][d2]));
            }
            x=fa[x][i];
        }
    }
    if(x==y)return myadd(nx[idy],g[y][idy]);
    for(int i=16;i>=0;i--)
    {
        if(dep[x]>=(1<<i) && fa[x][i]!=fa[y][i])
        {           
            tx[0]=nx[0],tx[1]=nx[1];nx[0]=nx[1]=-1;ty[0]=ny[0],ty[1]=ny[1];ny[0]=ny[1]=-1;
            for(int d1=0;d1<=1;d1++)
            {
                for(int d2=0;d2<=1;d2++)nx[d2]=mymin(nx[d2],myadd(tx[d1],p[x][i][d1][d2])),ny[d2]=mymin(ny[d2],myadd(ty[d1],p[y][i][d1][d2]));
            }           
            x=fa[x][i],y=fa[y][i];
        }
    }
    int P=fa[x][0];
    return mymin(myadd(myadd(nx[1],ny[1]),f[P][0]+g[P][0]-f[x][1]-f[y][1]),myadd(myadd(mymin(nx[0],nx[1]),mymin(ny[0],ny[1])),f[P][1]+g[P][1]-min(f[x][0],f[x][1])-min(f[y][0],f[y][1])));
}
char ss[5];
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int n=read(),m=read();scanf("%s",ss+1);
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        ins(x,y),ins(y,x);
    }
    memset(p,-1,sizeof(p));
    dfs(1),dp(1),pre(1);
    while(m--)
    {
        int x=read(),idx=read(),y=read(),idy=read();
        printf("%lld\n",solve(x,idx,y,idy));
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值