【NOIP2017提高A组冲刺11.1】荒诞

Description

给出一张n个点m条边的图,保证图中不存在长度大于10的简单路径
选择某一个点需要付出Ci的代价,求最小代价使得每个点都被选择,或者它相邻的点被选择

Solution

原题似乎是POI2012的某道题(夕立:poi?)
而题解的source似乎错了
这个条件显然是提示着我们要状压
但是二进制状态似乎不靠谱,我们要压三进制,表示某个点选/不选被覆盖/不选没被覆盖
然后沿着欧拉序转移就好了
看上去复杂度很不靠谱但是事实上能跑过23333

Code

#include 
#include 
#include 
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
using namespace std;

int read() {
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    int x=ch-'0';
    for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}

void updata(int &x,int y) {
    if (y<x) x=y;
}

const int N=5*1e4+5,S=6*1e4,inf=2e9;

int last[N],next[N],t[N],l;
void add(int x,int y) {
    t[++l]=y;next[l]=last[x];last[x]=l;
}

int n,m,x,y,id,c[N],dep[N],three[11];

void init() {
    n=read();m=read();
    fo(i,1,n) c[i]=read();
    fo(i,1,m) {
        x=read();y=read();
        add(x,y);add(y,x);
    }
    three[0]=1;fo(i,1,10) three[i]=three[i-1]*3;
}

int f[11][S],a[11],vis[N];
bool link[11];

void dfs(int x,int y) {
    vis[x]=id;dep[x]=dep[y]+1;
    rep(i,x) if (vis[t[i]]!=id) dfs(t[i],x);
}

void dp(int x,int y) {
    vis[x]=id;fo(i,1,10) link[i]=0;
    rep(i,x) if (dep[t[i]]x]) link[dep[t[i]]]=1;

    fo(s,0,three[dep[x]]-1) f[dep[x]][s]=inf;
    fo(s,0,three[dep[y]]-1) {
        if(f[dep[y]][s]==inf) continue;

        int ts=s;
        fo(i,1,dep[y]) a[i]=ts%3,ts/=3;

        bool pty=0;
        fo(i,1,dep[y]) pty|=link[i]&(a[i]==2);

        f[dep[x]][s+pty*three[dep[x]-1]]=f[dep[y]][s];

        ts=s+2*three[dep[x]-1];
        fo(i,1,dep[y]) if (link[i]&&!a[i]) ts+=three[i-1]; 

        updata(f[dep[x]][ts],f[dep[y]][s]+c[x]);
    }

    rep(i,x)
        if (vis[t[i]]!=id) {
            dp(t[i],x);
            fo(s,0,three[dep[x]]-1) {
                f[dep[x]][s]=f[dep[t[i]]][s+three[dep[t[i]]-1]];
                updata(f[dep[x]][s],f[dep[t[i]]][s+2*three[dep[t[i]]-1]]);
            }
        }
}

void solve() {
    int ans=0;
    fo(i,1,n)
        if (!vis[i]) {
            ++id;dfs(i,0);
            ++id;dp(i,0);
            ans+=min(f[1][1],f[1][2]);
        }
    printf("%d\n",ans);
}

int main() {
    freopen("absurdity.in","r",stdin);
    freopen("absurdity.out","w",stdout);
    init();
    solve();
    return 0;
}

你可能感兴趣的:(状压dp)