解题:POI 2008 Subdivision of Kingdom

Posted ydnhaha

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解题:POI 2008 Subdivision of Kingdom相关的知识,希望对你有一定的参考价值。

题面

还可以这么搜......学到了(PoPoQQQ orz)

我们最朴素的做法是枚举所有状态(当然可以剪,剪完最终实际状态量也是$C_{26}^{13}$的),然后每次$O(n)$扫一遍判断,大概会T炸,考虑优化

我们先预处理每个状态中$1$的数目和连边的状态,然后压缩状态初始让一边集合为空,一边集合为全集,这样每次从已有的点的前面$frac{n}{2}$个点中枚举一个加入另一边,就可以边搜边更新边数而不用最后$O(n)$检查了。另一个问题是数组可能非常大,这里我们可以把状态拆成前后两半,然后检查的时候检查两半再拼起来就好了。学了学技巧和思想还是挺好的说......

技术分享图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 int ss[28],cnt[1<<13];
 6 int n,m,t1,t2,ans=2e9,anss,num,half;
 7 int s(int x)
 8 {
 9     return 1<<(x-1);
10 }
11 int getst(int x)
12 {
13     return cnt[x&half]+cnt[x>>num];
14 }
15 void DFS(int last,int noww,int state,int numb)
16 {
17     if(noww>n) return ;
18     if(noww==num)
19     {
20         if(numb<ans)
21             ans=numb,anss=state;
22         return ;
23     }
24     for(int i=last;i<=n;i++)
25         DFS(i+1,noww+1,state|s(i),numb-getst(state&ss[i])+getst((~state)&ss[i]));
26 }
27 int main ()
28 {
29     scanf("%d%d",&n,&m);
30     num=n>>1,half=(1<<num)-1;
31     for(int i=1;i<=m;i++)
32     {
33         scanf("%d%d",&t1,&t2);
34         ss[t1]|=s(t2),ss[t2]|=s(t1);
35     }
36     for(int i=1;i<=half;i++)
37         cnt[i]=cnt[i>>1]+(i&1);
38     DFS(1,0,0,0);
39     for(int i=1;i<=n;i++)
40         if(anss&s(i)) printf("%d ",i);
41     return 0;
42 }
View Code

 

以上是关于解题:POI 2008 Subdivision of Kingdom的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1125:[POI2008]Poc

解题:POI 2010 Beads

[POI2006]MET-Subway - 解题报告

BZOJ1131: [POI2008]Sta

P3469 [POI2008]BLO-Blockade 割点 tarjan

P3478 [POI2008]STA-Station