zoukankan      html  css  js  c++  java
  • [BZOJ3257]树的难题

    [BZOJ3257]树的难题

    ​ 给出一个无根树。树有N个点,边有权值。每个点都有颜色,是黑色、白色、灰色这三种颜色之一,称为一棵三色树。

    ​ 可爱的Alice觉得,一个三色树为均衡的,当且仅当,树中不含有黑色结点或者含有至多一个白色节点。然而,给出的三色树可能并不满足这个性质。所以,Alice打算删去若干条边使得形成的森林中每棵树都是均衡的,花费的代价等于删去的边的权值之和。请你计算需要花费的代价最小是多少。多组数据。n<=3e5


    感觉挺显然的

    状态设(dp[i][0/1][0/1/2])表示与i连通的联通快中有0/1个黑点,有0/1/2个白点,其中吗,大于1个黑点视作1处理,大于2个白点视作2个处理,灰点无所谓,不影响答案所以不计入状态

    为了方便,记x1=0/1表示第二维,x2=0/1/2表示第三维

    本来我按照u的颜色乱七八糟写了一大堆还挂了,看了一下大佬的处理办法:

    分两种情况考虑:把当前u割出去/留着

    留着的情况很好考虑:枚举v之前的x1,x2和v的x1,x2,直接转移

    讲个细节的东西,对于第二维的两个x1,我们希望

    0+1=1+0=1和1+1=1

    所以两个x1或起来就行。

    对于x2,就没什么好办法,写个东西>2返回2就行

    保留的转移方程:

    [cmin(dp[u][x1|vx1][T(x2+vx2)],dp[u][x1][x2]+dp[v][vx1][vx2]) ]

    然后割掉的话需要满足的条件是,v自己是一个合法的子树

    那么

    [cmin(dp[u][x1][x2],dp[u][x1][x2]+dp[v][vx1][vx2]+w) ]

    还是老样子,对于每次枚举的v,用个新数组来转移,然后覆盖到dp[u]上


    #include<bits/stdc++.h>
    #define rep(i,x,y) for (int i=x;i<=y;i++)
    using namespace std;
    typedef long long ll;
    const int maxn=300010;
    const ll inf=1LL<<62;
    struct Edge{
        int v,nex,dis;
    }edge[maxn<<1];
    int cnt,head[maxn],col[maxn],n;
    ll dp[maxn][5][5],f[5][5];
    void addEdge(int u,int v,int d){
        edge[++cnt]=(Edge){v,head[u],d};head[u]=cnt;
    }
    /*
    dp[x][0/1][0/1/2]表示与x相连的连通块中
    有0或多个黑,0个,1个或多个白 的答案
    */
    inline int XT(int x){
        return x>2?2:x;
    }
    inline void cmin(ll &x,ll y){
        if (y<x) x=y;
    }
    void dfs(int u,int fa){
        dp[u][col[u]==0][col[u]==1]=0;
        for (int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v,w=edge[i].dis;
            if (v==fa) continue;
            dfs(v,u);
            rep(x1,0,1) rep(x2,0,2) f[x1][x2]=inf;
            rep(x1,0,1) rep(x2,0,2) if (dp[u][x1][x2]<inf)
                rep(vx1,0,1) rep(vx2,0,2) if (dp[v][vx1][vx2]<inf){
                    cmin(f[x1|vx1][XT(x2+vx2)],dp[u][x1][x2]+dp[v][vx1][vx2]);
                    if ((vx1==0) || vx2<2) cmin(f[x1][x2],dp[u][x1][x2]+dp[v][vx1][vx2]+w);
                }
            memcpy(dp[u],f,sizeof(dp[u]));
        }
    }
    int read(){
        int x=0;char ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        while (isdigit(ch)) x=x*10+ch-48,ch=getchar();
        return x;
    }
    int main(){
        int T=read();
        while (T--){
            rep(i,1,maxn-1) rep(x1,0,1) rep(x2,0,2) dp[i][x1][x2]=inf;
            memset(head,0,sizeof(head));
            memset(edge,0,sizeof(edge));
            memset(col,0,sizeof(col));
            cnt=0;
            n=read();
            rep(i,1,n) col[i]=read();
            rep(i,1,n-1){
                int x=read(),y=read(),d=read();
                addEdge(x,y,d);addEdge(y,x,d);
            }
            dfs(1,0);
            ll ans=inf;
            rep(x2,0,2) ans=min(ans,dp[1][0][x2]);
            rep(x2,0,1) ans=min(ans,dp[1][1][x2]);
          	 printf("%lld
    ",ans);
        }
        //getchar();
        return 0;
    }
    
    

    反思和要修的锅:

    1、为啥保留的时候不用注意dp[u][x1][x2]与dp[v][vx1][vx2]并起来时是否合法?

    2、割掉的时候那个转移的式子感觉显然不会调用啊,(dp[u][x1][x2]+dp[v][vx1][vx2]+w)感觉始终>dp[u][x1][x2],不知道为啥会转移?

  • 相关阅读:
    VMware6.0-vCenter的安装准备及安装
    VeeamBackup9.5安装与配置
    VeeamOne(Free Edition 9.5 )-安装与配置
    UIDatePicker
    UIImagePicker照片选择器
    UIImageView
    UILabel
    UIScrollView 期本使用方法
    UISegment
    UISlide
  • 原文地址:https://www.cnblogs.com/ugly-CYW-lyr-ddd/p/11802245.html
Copyright © 2011-2022 走看看