BZOJ5466 NOIP2018保卫王国(倍增+树形dp)

  暴力dp非常显然,设f[i][0/1]表示i号点不选/选时i子树内的答案,则f[i][0]=Σf[son][1],f[i][1]=a[i]+Σmin(f[son][0],f[son][1])。

  注意到B的部分分,可以想到每次修改只会对修改点到根的路径上的点的dp值产生影响。

  考虑如何优化修改路径这一过程,先看只修改一个点的情况。

  由于每次修改并非累积,事实上应该考虑一种预处理来快速得到答案,并且发现其实最终我们只需要f[root][]。容易想到倍增。设f[x][k][0/1][0/1]表示x号点为0/1时其2k级祖先为0/1时这条链上的答案,即其2k级祖先的子树-x的子树的答案。这个东西本身就是可减的,即知道了在x号点子树内的y点选/不选的情况下x子树的答案、y号点选/不选的情况下y子树的答案,将其相减就是x子树去掉y子树的答案。

  倍增数组并不难求,显然我们已经有f[x][0][][],在2k-1级祖先那里合并得到2k级的答案,考虑2k-1级祖先选还是不选取个min即可,大约就是floyd/矩乘。回答询问同样也是类似的很正常的倍增。那么只改一个点就能做了。

  再考虑改两个点,其实基本类似。两个点倍增求出到他们的lca下方一个点的答案,以此更新lca答案,再从lca倍增跳到根即可。对其中一点是另一点祖先的情况最好特判。听起来不是很复杂但写起来得考虑清楚。

  另一种做法是ddp,暂时觉得不太学的动,好像也很久没学新姿势了。感觉倍增做法看上去还是比较noip的,虽然考场上被神仙t2和完全没碰过但知道能做这个题的ddp冲昏头脑肯定想不出来。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define inf 100000000000ll
#define N 100010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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;
}
int n,m,a[N],p[N],fa[N][18],deep[N],t;
ll f[N][2],g[N][18][2][2];
struct data{int to,nxt,len;
}edge[N<<1];
struct data2{ll x,y;int id;};
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k)
{
    f[k][0]=0,f[k][1]=a[k];
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=fa[k][0])
    {
        deep[edge[i].to]=deep[k]+1;
        fa[edge[i].to][0]=k;
        dfs(edge[i].to);
        f[k][0]+=f[edge[i].to][1];
        f[k][1]+=min(f[edge[i].to][0],f[edge[i].to][1]);
    }
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=fa[k][0])
    {
        g[edge[i].to][0][0][0]=inf;
        g[edge[i].to][0][0][1]=f[k][1]-min(f[edge[i].to][0],f[edge[i].to][1]);
        g[edge[i].to][0][1][0]=f[k][0]-f[edge[i].to][1];
        g[edge[i].to][0][1][1]=f[k][1]-min(f[edge[i].to][0],f[edge[i].to][1]);
    }
}
void pre()
{
    for (int j=1;j<18;j++)
    {
        for (int i=1;i<=n;i++)
        fa[i][j]=fa[fa[i][j-1]][j-1];
        for (int i=1;i<=n;i++)
            for (int x=0;x<2;x++)
                for (int y=0;y<2;y++)
                g[i][j][x][y]=min(g[i][j-1][x][0]+g[fa[i][j-1]][j-1][0][y],g[i][j-1][x][1]+g[fa[i][j-1]][j-1][1][y]);
    }
}
int lca(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    for (int j=17;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j];
    if (x==y) return x;
    for (int j=17;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
data2 query(int x,int root,int isup,ll tx,ll ty)
{
    data2 u;u.x=tx,u.y=ty;
    for (int j=17;~j;j--)
    if (deep[fa[x][j]]>deep[root])
    {
        tx=u.x,ty=u.y;
        u.x=min(tx+g[x][j][0][0],ty+g[x][j][1][0]);
        u.y=min(tx+g[x][j][0][1],ty+g[x][j][1][1]);
        x=fa[x][j];
    }
    u.id=x;
    if (isup)
    {
        tx=u.x,ty=u.y;
        u.x=min(tx+g[x][0][0][0],ty+g[x][0][1][0]);
        u.y=min(tx+g[x][0][0][1],ty+g[x][0][1][1]);
    }
    return u;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5466.in","r",stdin);
    freopen("bzoj5466.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read();read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();
        addedge(x,y),addedge(y,x);
    }
    fa[1][0]=1;dfs(1);
    pre();
    while (m--)
    {
        int x=read(),opx=read(),y=read(),opy=read();
        if (deep[x]<deep[y]) swap(x,y),swap(opx,opy);
        if (opx==0&&opy==0&&fa[x][0]==y) {printf("-1\n");continue;}
        int k=lca(x,y);
        if (k==y)
        {
            data2 u=query(x,k,0,opx==0?f[x][0]:inf,opx==1?f[x][1]:inf);
            ll tx=u.x,ty=u.y;
            u.x=f[k][0]+ty-f[u.id][1];
            u.y=f[k][1]+min(tx,ty)-min(f[u.id][0],f[u.id][1]);
            if (opy==0) u.y=inf;else u.x=inf;
            if (k!=1) u=query(k,1,1,u.x,u.y);
            printf(LL,min(u.x,u.y));
        }
        else
        {
            data2 u=query(x,k,0,opx==0?f[x][0]:inf,opx==1?f[x][1]:inf);
            data2 v=query(y,k,0,opy==0?f[y][0]:inf,opy==1?f[y][1]:inf);
            data2 w;
            w.x=f[k][0]+u.y-f[u.id][1]+v.y-f[v.id][1];
            w.y=f[k][1]+min(u.x,u.y)-min(f[u.id][0],f[u.id][1])+min(v.x,v.y)-min(f[v.id][0],f[v.id][1]);
            w=query(k,1,1,w.x,w.y);
            printf(LL,min(w.x,w.y));
        }
    }
    return 0;
}

猜你喜欢

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