zoukankan      html  css  js  c++  java
  • NOIP2018保卫王国

    题目大意:给一颗有点权的树,每次规定两个点选还是不选,求这棵树的最小权点覆盖。

    题解

    ZZ码农题。

    要用动态dp做,这题就是板子,然鹅并不会,留坑代填。

    因为没有修改,所以可以静态倍增。

    我们先做一遍正常的树形dp,求出g[i][0/1]0/1表示当前节点选或不选。

    然后我们再倒腾出一个数组l[i][0/1]表示从当前点作为根,再扣掉当前子树的答案。

    然后倍增处理dp[i][j][0/1][0/1]表示从i向上2i长度的链,起点和终点的选择情况,表示以下区域的答案。


    比如这条黑色的链,它表示的是黄圈里的所有点的答案。

    然后对于一个询问,我们可以跳LCA,边跳变统计答案,这样我们就可以统计出以LCA为根的子树的答案,在加上之前处理过的l数组的答案,就可以吧答案算全了。

    NOIP考这种****题有意思吗?

    代码

    #include<iostream>
    #include<cstdio>
    #define N 100009
    using namespace std;
    typedef long long ll;
    int tot,head[N],deep[N],p[N][20],n,m;
    char typef[5];
    ll dp[N][19][2][2],w[N],f[2][2],g[2][2],pr[N][2],lian[N][2],pr2[N][2];
    inline int rd(){
        int x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    struct node{int n,to;}e[N<<1];
    inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
    inline void hb(int x,int y,int l){
        dp[x][l][0][0]=min(dp[x][l-1][0][1]+dp[y][l-1][1][0],dp[x][l-1][0][0]+dp[y][l-1][0][0]);
        dp[x][l][1][0]=min(dp[x][l-1][1][1]+dp[y][l-1][1][0],dp[x][l-1][1][0]+dp[y][l-1][0][0]);
        dp[x][l][0][1]=min(dp[x][l-1][0][1]+dp[y][l-1][1][1],dp[x][l-1][0][0]+dp[y][l-1][0][1]);
        dp[x][l][1][1]=min(dp[x][l-1][1][1]+dp[y][l-1][1][1],dp[x][l-1][1][0]+dp[y][l-1][0][1]);
    }
    void dfs(int u,int fa){
        for(int i=1;(1<<i)<=deep[u];++i){
            p[u][i]=p[p[u][i-1]][i-1];
            hb(u,p[u][i-1],i);
        }
        for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
            int v=e[i].to;deep[v]=deep[u]+1;p[v][0]=u;
            dp[v][0][0][1]=pr[u][1]-min(pr[v][1],pr[v][0]);
            dp[v][0][1][0]=pr[u][0]-pr[v][1];
            dp[v][0][1][1]=pr[u][1]-min(pr[v][1],pr[v][0]);
            dfs(v,u);
        }
    }
    void predfs(int u,int fa){
        pr[u][0]=0;pr[u][1]=w[u];
        for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
            int v=e[i].to;predfs(v,u);
            pr[u][0]+=pr[v][1];pr[u][1]+=min(pr[v][0],pr[v][1]);
        }
    }
    void dfs2(int u,int fa){
        for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
            int v=e[i].to;
        //    pr2[v][0]=pr2[u][1];pr2[v][1]=min(pr2[u][0],pr2[u][1]);
        //    lian[v][0]=pr2[v][0]-pr[v][0];lian[v][1]=pr2[v][1]-pr[v][1];
           lian[v][0]=lian[u][1]+pr[u][1]-min(pr[v][0],pr[v][1]);
           lian[v][1]=min(lian[v][0],lian[u][0]+pr[u][0]-pr[v][1]);
            dfs2(v,u);
        }
    }
    inline ll getlca(int a,int b,int x,int y){
        if(deep[a]<deep[b])swap(a,b),swap(x,y);
    //    cout<<a<<" "<<b<<" "<<x<<" "<<y<<endl;
        ll ans=pr[a][x]; //cout<<ans<<endl;
        f[0][x]=0;int now=1,pre=0;
        for(int i=18;i>=0;--i)if(deep[a]-(1<<i)>=deep[b]){
            f[now][1]=min(f[pre][0]+dp[a][i][0][1],f[pre][1]+dp[a][i][1][1]);
            f[now][0]=min(f[pre][0]+dp[a][i][0][0],f[pre][1]+dp[a][i][1][0]);
            swap(now,pre);a=p[a][i];
        }
        if(a==b)return ans+f[pre][y]+lian[a][y];
        g[pre][y]=0;ans+=pr[b][y];//cout<<ans<<endl;
        for(int i=18;i>=0;--i)if(p[a][i]!=p[b][i]){
            f[now][1]=min(f[pre][0]+dp[a][i][0][1],f[pre][1]+dp[a][i][1][1]);
            f[now][0]=min(f[pre][0]+dp[a][i][0][0],f[pre][1]+dp[a][i][1][0]);
            g[now][1]=min(g[pre][0]+dp[b][i][0][1],g[pre][1]+dp[b][i][1][1]);
            g[now][0]=min(g[pre][0]+dp[b][i][0][0],g[pre][1]+dp[b][i][1][0]);
            swap(now,pre);a=p[a][i];b=p[b][i];
        }
        swap(now,pre);
        ll ans1=f[now][1]+g[now][1]+pr[p[a][0]][0]-pr[a][1]-pr[b][1];
        ll ans2=min(f[now][1],f[now][0])+min(g[now][0],g[now][1])+pr[p[a][0]][1]-min(pr[a][0],pr[a][1])-min(pr[b][0],pr[b][1]);
        return ans+min(ans1+lian[p[a][0]][0],ans2+lian[p[a][0]][1]);
    }
    int main(){
        n=rd();m=rd();scanf("%s",typef);int u,v;
        for(int i=1;i<=n;++i)w[i]=rd();
        for(int i=1;i<=n;++i)
          for(int j=0;j<=18;++j)
           for(int k=0;k<=1;++k)for(int l=0;l<=1;++l)dp[i][j][k][l]=1e12;
        for(int i=1;i<n;++i){u=rd();v=rd();add(u,v);add(v,u);}
        predfs(1,0);
        pr2[1][1]=pr[1][1];pr2[1][0]=pr[1][0];dfs2(1,0);
        dfs(1,0);int a,x,b,y;
    /*    for(int i=1;i<=n;++i){
            cout<<i<<" ";
          for(int j=0;j<=1;++j)cout<<lian[i][j]<<" ";cout<<endl; 
        }*/
      /*  for(int i=1;i<=n;++i){
            cout<<i<<endl;
            for(int j=0;(1<<j)<=deep[i];++j)cout<<dp[i][j][0][0]<<" "<<dp[i][j][0][1]<<" "<<dp[i][j][1][0]<<" "<<dp[i][j][1][1]<<endl;
        }*/
        while(m--){
            a=rd();x=rd();b=rd();y=rd(); 
            for(int i=0;i<=1;++i)for(int j=0;j<=1;++j)f[i][j]=g[i][j]=1e12;
            ll ans=getlca(a,b,x,y);
            if(ans>1e10)puts("-1");else printf("%lld
    ",ans);
        }
        return 0;
    } 
  • 相关阅读:
    Activit 5.13 工作流部署新版本后回退到上一个版本
    一个java的http请求的封装工具类
    FastJSON使用例子
    SoapUI、Postman测试WebService
    PLSQL连接oracle数据库
    python函数修饰符@的使用
    QEMU KVM Libvirt手册(8): 半虚拟化设备virtio
    QEMU KVM Libvirt手册(7): 硬件虚拟化
    多个router和多个network
    nova file injection的原理和调试过程
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10260557.html
Copyright © 2011-2022 走看看