【简】题解 P4297 [NOI2006]网络收费

传送门:P4297 [NOI2006]网络收费 

题目大意:

给定一棵满二叉树,每个叶节点有一个状态(0,1),任选两个叶节点,如果这两个叶节点状态相同但他们的LCA所管辖的子树中的与他们状态相同的叶节点个数较少(少于1/2),则会产生2f的代价,如果状态不同,则产生f的代价,如果状态相同且LCA管辖子树中与他们状态相同叶节点个数较多,则不产生代价,现在每个节点可以变更状态,但变更状态也有自己的代价,求最小总代价(来自leozhang大佬)

QWQ:

因为各个点互相之间的贡献由各个点之间的LCA的状态

所以考虑从根节点枚举当前点是A多还是B多的状态

因为直接再枚举其子节点的AB个数dfs显然不合理

考虑从子节点往上枚举AB个数做背包

显然各节点贡献可以分开计算

因为显然不可能在统计该节点时记录叶子节点的所有状态

所以提前计算所有叶子节点费用

记录每个叶子节点在他各种祖先的不同状态的贡献

计算叶子节点贡献时算一遍就好了

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define C getchar()-48
inline ll read()
{
    ll s=0,r=1;
    char c=C;
    for(;c<0||c>9;c=C) if(c==-3) r=-1;
    for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
    return s*r;
} 
const ll N=2060,inf=1ll<<60;
ll n,gs,ans=inf;
ll f[N][2],a[N],dp[N][N],c[N][12],pd[20];
inline int lca(int a,int b)
{
    for(int i=n-1;i>=0;i--) if((a>>i)!=(b>>i)) return n-i-1;
}
inline void dfs(int x,int cs)
{
    for(int i=0;i<=1<<n;i++) dp[x][i]=inf;
    if(cs==n)
    {
        dp[x][0]=f[x-gs][0];
        dp[x][1]=f[x-gs][1];
        for(int i=0;i<n;i++) dp[x][pd[i]^1]+=c[x-gs][i];
        return;
    }
    pd[cs]=1;dfs(x<<1,cs+1),dfs(x<<1|1,cs+1);
    for(int i=0;i<=1<<(n-cs-1);i++)   
    for(int j=(1<<(n-cs-1))+1-i;j<=1<<(n-cs-1);j++)  
      dp[x][i+j]=min(dp[x][i+j],dp[x<<1][i]+dp[x<<1|1][j]);
      
    pd[cs]=0;dfs(x<<1,cs+1),dfs(x<<1|1,cs+1);
    for(int i=0;i<=1<<(n-cs-1);i++)   
    for(int j=0;i+j<=1<<(n-cs-1);j++) 
      dp[x][i+j]=min(dp[x][i+j],dp[x<<1][i]+dp[x<<1|1][j]);
}
int main()
{
    n=read();gs=(1<<n);
    for(int i=0;i<gs;i++) a[i]=read();
    for(int i=0;i<gs;i++) f[i][a[i]]=0,f[i][a[i]^1]=read();
    for(int i=0;i<gs;i++)  
    for(int j=i+1;j<gs;j++)
    {
        int rt=lca(i,j),v=read();
        c[i][rt]+=v,c[j][rt]+=v;
    }
    dfs(1,0);
    for(int i=0;i<=gs;i++) ans=min(ans,dp[1][i]); 
    cout<<ans;
    return 0;
}
原文地址:https://www.cnblogs.com/1436177712qqcom/p/11135189.html