Description
有一个n个点,m条边的无向图,选择第i个点的代价为ci
特别地,这张图中的任意两点间不存在节点数超过10的简单路径。
要求选择一些点,使得最后每个点要么自己被选,要么直接相连中的点至少有一个被选,且使得代价和最小
对于100%的测试点,满足1<=n<=2*10^4,0<=m<=2.5*10^4,0<=ci<=10^4
Analysis
这是一道把npc问题带上特殊性质,从而使得有一些奇技能做的题
考虑利用题目中加粗条件。这个条件意味着每个联通块的dfs树深度不超过10
10很小,由于是个图,条件很难满足,我们可以考虑状压,把所有点状态存起来
设f[i][s]表示,当前做到第i个点,它到根的路径上的点的状态为s的代价(为什么不是子树,因为子树里的点数量没有限制)
s是三进制状态,0/1/2分别表示 未选且未被覆盖/未选且被覆盖/选
(如果是二进制你会发现做不了)
我们发现这样可以从上到下dp了!
考虑怎样利用f来满足条件
一个一个子树做,做完了就把满足该子树的代价上传回来,然后再下去做第二个子树(此时f里面已经包含了做完第一个子树所需代价了)
最后复杂度为
O(n∗310∗10)
,实测跑不到
我们不能被复杂度吓跑,而应该套路地去思考
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define efo(i,v,u) for(int i=last[v],u=to[last[v]];i;i=next[i],u=to[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
char ch;
void read(int &n)
{
int t=0;int p=1;
for(ch=getchar();!('0'<=ch && ch<='9');ch=getchar())if(ch=='0') p=-1;
for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';
n=t*p;
}
const int N=5e4+6,M=5e4+5,INF=2139062143;
int n,m,ans,_3[13],c[N],tot,to[M],next[M],last[N];
void link(int u,int v){to[++tot]=v,next[tot]=last[u],last[u]=tot;}
bool vis[N],ok[13];
int a[13],dep[N],f[2][100000];
void dfs(int v,int fr,int d)
{
a[d]=v,dep[v]=d,vis[v]=1;
bool q=d&1;
efo(i,v,u) if(u!=fr && !vis[u])
{
mset(ok,0);
mset(f[q^1],127);
efo(i,u,p) if(vis[p]) ok[dep[p]]=1;
fo(s,0,_3[d+1]-1) if(f[q][s]!=INF)
{
int s1=s;
fo(j,0,d) if((s/_3[j])%3==2)
if(ok[j]) {s1+=_3[d+1];break;}
f[q^1][s1]=f[q][s];
s1=s+2*_3[d+1];
fo(j,0,d) if(ok[j] && (s/_3[j])%3==0) s1+=_3[j];
f[q^1][s1]=min(f[q^1][s1],f[q][s]+c[u]);
}
dfs(u,v,d+1);
fo(s,0,_3[d+1]-1) f[q][s]=min(f[q^1][s+_3[d+1]],f[q^1][s+2*_3[d+1]]);
}
}
int main()
{
freopen("absurdity.in","r",stdin);
freopen("absurdity.out","w",stdout);
_3[0]=1;
fo(i,1,12) _3[i]=_3[i-1]*3;
int u,v;
read(n),read(m);
fo(i,1,n) read(c[i]);
fo(i,1,m) read(u),read(v),link(u,v),link(v,u);
int ans=0;
fo(i,1,n)
if(!vis[i])
{
mset(f[0],127);
f[0][0]=0,f[0][2]=c[i];
dfs(i,i,0);
ans+=min(f[0][1],f[0][2]);
}
printf("%d",ans);
return 0;
}