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

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);
}
发布了257 篇原创文章 · 获赞 451 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/crybymyself/article/details/78415872