【简】题解 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; }