题意:我有一个n个点,m条边的无向图,第i个点建立一个旅游站点的费用是c_i。特别地,这张图中的任意两点间不存在节点数超过10的简单路径。
为了把一切都做得完善,为了使我感到不那么孤独,我想要建造一些旅游站点使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点建立了旅游站点。我还希望这个建造方案总花费尽量少。
请求出这个花费。
czy大爷出的好题。
考场上有正解方向的想法,细节一脸懵逼。
首先15分的暴力DP(保证都是树)的思路可以应用到正解中。
10保证了每个连通块的最大深度不超过10,那么可以状压。
如同暴力DP一样,我们设0/1/2表示这个点未被覆盖/已被覆盖未选/已选。
然后考虑如何处理返祖边。
按照欧拉序遍历每个连通块,对于当前点x,枚举到状态s,考虑他的方案。
假如x不选择,那么如果x的祖先(包括他的父亲)有返祖边和他相连且状态为2,那么就可以转移到状态
s+3dep[x]−1
含义是把x这个位置变为1.
否则就只能转移到状态s。
假如选择x,考虑返祖边,对于他的祖先(不包括他的父亲,或者说要单独拿出来处理),x状态肯定为2,他的祖先的状态也会因此而改变,原来为0的就会变成1.
这个地方单独记录一个ss表示选择以后他的祖先的状态改变,这里标解的实现非常巧妙,就是用一个桶来记录他的祖先状态,感觉非常perfect。
然后最后遍历他的儿子,注意到儿子会改变祖先的状态,所以我们在处理完他的子树以后要重新计算f,这里就很简单了。
f[x][s]=min(f[x][s+3dep[x]−1∗2],f[x][s+3dep[x]−1∗1])
表示当前点选或者不选。
答案就是
min(f[root][1],f[root][2])
.
最后注意一下边界,那些地方是dep[x],那些地方是dep[x]-1,考虑一下要不要枚举父亲即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
int n,m;
const int inf=0x3f3f3f3f;
const int M=6e4+5;
bool vis[N];
int head[N],next[N],go[N],ans;
int f[2][M],c[N],bin[20],fa[N],dep[N];
inline void pre(int x)
{
vis[x]=1;
dep[x]=dep[fa[x]]+1;
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (!vis[v])
{
fa[v]=x;
pre(v);
}
}
}
int tot;
inline void add(int x,int y)
{
go[++tot]=y;
next[tot]=head[x];
head[x]=tot;
}
inline void dfs(int x)
{
int now=dep[x]&1;
bool flag[12];
memset(flag,0,sizeof(flag));
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (dep[v]<dep[x])
flag[dep[v]-1]=1;
}
fo(i,0,bin[dep[x]])f[now][i]=inf;
fo(s,0,bin[dep[x]-1]-1)
{
if (f[now^1][s]>=inf)continue;
int ss=s+1*bin[dep[x]-1];
bool bz=0;
fo(i,0,dep[x]-2)
{
if (flag[i])
{
if (!(ss/bin[i]%3))ss=ss+bin[i]*2;
if ((s/bin[i]%3)==1)bz=1;
}
}
f[now][ss]=min(f[now][ss],f[now^1][s]+c[x]);
f[now][s+bin[dep[x]-1]*2*bz]=f[now^1][s];
}
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (fa[v]==x)
{
dfs(v);
fo(s,0,bin[dep[x]]-1)
f[now][s]=min(f[now^1][s+1*bin[dep[x]]],f[now^1][s+2*bin[dep[x]]]);
}
}
}
int main()
{
freopen("absurdity.in","r",stdin);
freopen("absurdity.out","w",stdout);
scanf("%d%d",&n,&m);
bin[0]=1;
fo(i,1,11)bin[i]=bin[i-1]*3;
fo(i,1,n)scanf("%d",&c[i]);
fo(i,1,m)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
int ans=0;
fo(i,1,n)
{
if (!vis[i])
{
fa[i]=i;
pre(i);
memset(f,0x3f,sizeof(f));
f[0][0]=0;
dfs(i);
ans+=min(f[1][1],f[1][2]);
}
}
printf("%d\n",ans);
}