荒诞
【问题描述】
我的灵魂与我之间的距离如此遥远,而我的存在却如此真实。
——加缪《局外人》
我醒来的时候,发现满天星斗照在我的脸上。田野上的声音一直传到我的耳
畔。夜的气味,土地的气味,海盐的气味,使我的两鬓感到清凉。这沉睡的夏夜
的奇妙安静,像潮水一般浸透我的全身。这时,长夜将尽,汽笛叫了起来。它宣
告有些人踏上旅途,要去一个从此和我无关痛痒的世界。
这时我在想一个问题:我有一个 n 个点,m 条边的无向图,第 i 个点建立一
个旅游站点的费用是 c_i。特别地,这张图中的任意两点间不存在节点数超过 10
的简单路径。
为了把一切都做得完善,为了使我感到不那么孤独,我想要建造一些旅游站
点使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点
建立了旅游站点。我还希望这个建造方案总花费尽量少。
请求出这个花费。
【输入格式】
文件第一行有两个正整数 n 和 m。
第二行包含 n 个整数,其中第 i 个数为 c_i,表示在第 i 个点建立旅游站点
的费用。
接下来 m 行,每行两个正整数 u,v,表示一条边(u,v),保证没有重边。
【输出格式】
输出只有一行,表示最小的总花费。
【样例输入输出】
absurdity.in
6 6
3 8 5 6 2 2
1 2
2 3
1 3
3 4
4 5
4 6
absurdity.out
7
【数据范围】
对于前 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。
很鬼的一道题
两点之间路径点数不超过10,意味着联通块的DFS树深度<=10
现在进行树形dp的唯一难点在于一个点会与祖先相互影响,于是考虑状压
f[i][S]表示第i层(x号点)祖先状态为S的最小方案
S是一个3进制数,0表示未选未覆盖,1表示已选,2表示未选被覆盖
我们枚举x连向祖先的边,如果一个祖先状态为1,说明x可以不选
f[i][S+2*pw[i-1]]=min(f[i-1][S])
我们设S'为选x的状态,S'=S+pw[i-1]
如果一个祖先状态为0,那么把它填成2:S'=S'+2*pw[j-1]
于是我们又得到了选x的状态
f[i][S']=min(f[i-1][S]+cost[x])
因为还要考虑子孙对x的影响,所以还要在递归子树后转移
f[i][S]=min(f[i+1][S+pw[i]],f[i+1][S+2*pw[i]])
即子节点被控制
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct Node 7 { 8 int next,to; 9 }edge[50001]; 10 int c[10001]; 11 int head[10001],num,n,fa[10001],dep[10001],f[2][60000],INF,pw[15],m,ans; 12 bool vis[10001]; 13 void add(int u,int v) 14 { 15 num++; 16 edge[num].next=head[u]; 17 head[u]=num; 18 edge[num].to=v; 19 } 20 void dfs(int x) 21 {int i; 22 vis[x]=1; 23 for (i=head[x];i;i=edge[i].next) 24 { 25 int v=edge[i].to; 26 if (vis[v]==0) 27 { 28 fa[v]=x; 29 dep[v]=dep[x]+1; 30 dfs(v); 31 } 32 } 33 } 34 void dp(int x) 35 {int i,j,s; 36 bool link[11]; 37 int cur=dep[x]&1; 38 memset(f[cur],127/2,sizeof(f[cur])); 39 INF=f[cur][0]; 40 memset(link,0,sizeof(link)); 41 for (i=head[x];i;i=edge[i].next) 42 { 43 int v=edge[i].to; 44 if (dep[v]<dep[x]) link[dep[v]-1]=1; 45 } 46 for (s=0;s<pw[dep[x]-1];s++) 47 { 48 if (f[cur^1][s]>=INF) continue; 49 int ss=s+pw[dep[x]-1]; 50 bool flag=0; 51 for (i=0;i<dep[x]-1;i++) 52 if (link[i]) 53 { 54 if (!(ss/pw[i]%3)) ss+=pw[i]*2; 55 if ((s/pw[i]%3)==1) flag=1; 56 } 57 f[cur][ss]=min(f[cur][ss],f[cur^1][s]+c[x]); 58 f[cur][s+flag*2*pw[dep[x]-1]]=min(f[cur][s+flag*2*pw[dep[x]-1]],f[cur^1][s]); 59 } 60 for (i=head[x];i;i=edge[i].next) 61 { 62 int v=edge[i].to; 63 if (fa[v]==x) 64 { 65 dp(v); 66 for (s=0;s<pw[dep[x]];s++) 67 f[cur][s]=min(f[cur^1][s+pw[dep[x]]],f[cur^1][s+2*pw[dep[x]]]); 68 } 69 } 70 } 71 int main() 72 {int i,u,v; 73 cin>>n>>m; 74 pw[0]=1; 75 for (i=1;i<=10;i++) 76 pw[i]=pw[i-1]*3; 77 for (i=1;i<=n;i++) 78 { 79 scanf("%d",&c[i]); 80 } 81 for (i=1;i<=m;i++) 82 { 83 scanf("%d%d",&u,&v); 84 add(u,v);add(v,u); 85 } 86 for (i=1;i<=n;i++) 87 if (vis[i]==0) 88 { 89 dep[i]=1; 90 dfs(i); 91 f[0][0]=0; 92 dp(i); 93 ans+=min(f[1][1],f[1][2]); 94 memset(f,127/2,sizeof(f)); 95 } 96 cout<<ans; 97 }