• 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
    ";
    #else
        const char LL[]="%lld
    ";
    #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
    ");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;
    }
  • 相关阅读:
    PHP 求多个数组的笛卡尔积,适用于求商品规格组合 【递归思想, 类似广度优先搜索】【原创】
    CCF推荐期刊会议
    SCI分区
    值和指针接收者的区别
    程序员练级攻略
    保险
    golang 有缓冲channel和无缓冲channel
    后台学习路线
    golang之反射
    atomic和mutex
  • 原文地址:https://www.cnblogs.com/Gloid/p/10022777.html
Copyright © 2020-2023  润新知