您的位置:首页 > 其它

[置顶] 【JZOJ5442】【NOIP2017提高A组冲刺11.1】荒诞[状压dp]

2017-11-01 16:50 393 查看

Description

我的灵魂与我之间的距离如此遥远,而我的存在却如此真实。

——加缪《局外人》

我醒来的时候,发现满天星斗照在我的脸上。田野上的声音一直传到我的耳畔。夜的气味,土地的气味,海盐的气味,使我的两鬓感到清凉。这沉睡的夏夜的奇妙安静,像潮水一般浸透我的全身。这时,长夜将尽,汽笛叫了起来。它宣告有些人踏上旅途,要去一个从此和我无关痛痒的世界。

这时我在想一个问题:我有一个n个点,m条边的无向图,第i个点建立一个旅游站点的费用是c_i。特别地,这张图中的任意两点间不存在节点数超过10的简单路径。

为了把一切都做得完善,为了使我感到不那么孤独,我想要建造一些旅游站点使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点建立了旅游站点。我还希望这个建造方案总花费尽量少。

请求出这个花费。

Data Constraint

对于前10%的测试点,满足所有的c_i相等。

对于前30%的测试点,满足1<=n<=20,0<=m<=50。

对于另外15%的测试点,满足每个连通块都是一棵树。

对于100%的测试点,满足1<=n<=2*10^4,0<=m<=2.5*10^4,0<=c_i<=10^4。

Solution

另外15%的测试点相信很基础,我们设一个f[x][0..2]表示当前以x为根的子树内的最小代价。0表示x不选并且没被覆盖,1表示x不选但被覆盖,2表示选了x。前30分告诉我们可以用状压。100分我们沿用这两个算法。由于任意两点间不存在节点数超过10的简单路径,这说明我们若做一棵dfs树,树的深度不会超过10,我们考虑状压。设f[x][S]表示当前做到第x个节点,根到x的节点的3进制状态为S的最小代价。第i位0表示第i个点不选并且没被覆盖,1表示不选但被覆盖,2表示选了。我们每次由父亲转移至儿子时,我们可以枚举状态S,将x选择的贡献、x被覆盖、x没被选择覆盖的加入考虑形成S’,当我们做完儿子y返回至父亲x时,由于S内只能存储根到x的节点,但我们又不能忽略y节点,以后的转移中y必须被覆盖或选择。所以我们将x的f[x][S]修改为f[y][S+3^y]和f[y][S+2*3^y]的最小值,即y要么被覆盖要么被选择。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5e4+5,maxn1=59049;
int first[maxn],last[maxn],next[maxn],f[2][maxn1],a[maxn],bz[maxn],er[maxn],d[maxn];
int n,m,i,t,j,k,l,x,y,z,num,p,ln,ans,mx;
void lian(int x,int y){
last[++num]=y;next[num]=first[x];first[x]=num;
}
void dg(int x,int y,int p){
int t;
d[x]=++num;bz[x]=1;
memset(f[p],127,sizeof(f[p]));
for (i=0;i<er[num];i++){
if (f[1-p][i]==mx) continue;
f[p][i]=f[1-p][i];l=i+2*er[num];
for (t=first[x];t;t=next[t]){
if (!bz[last[t]]) continue;
k=i/er[d[last[t]]];
if (k%3==0) l+=er[d[last[t]]];
if (k%3==2) f[p][i+er[num]]=min(f[p][i+er[num]],f[1-p][i]);
}
f[p][l]=min(f[p][l],f[1-p][i]+a[x]);
}
for (t=first[x];t;t=next[t]){
if (bz[last[t]]) continue;
dg(last[t],x,1-p);
for (i=0;i<er[num];i++)f[p][i]=min(f[1-p][i+er[num]*2],f[1-p][i+er[num]]);
num--;
}
}
int main(){
freopen("absurdity.in","r",stdin);freopen("absurdity.out","w",stdout);
scanf("%d%d",&n,&m);er[1]=1;
for (i=2;i<=10;i++)er[i]=er[i-1]*3;
for (i=1;i<=n;i++)scanf("%d",&a[i]);
for (i=1;i<=m;i++)scanf("%d%d",&x,&y),lian(x,y),lian(y,x);
ln=er[10]-1;num=0;
for (i=1;i<=n;i++){
if (bz[i]) continue;
memset(f[0],127,sizeof(f[0]));f[0][0]=0;mx=f[0][1];num=0;
dg(i,0,1);
ans+=min(f[1][2],f[1][1]);
}
printf("%d\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: