bzoj 1015 星球大战starwar

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1015

题解:

  如果按照题目的意思,每次删点、删边太困难了……于是采用逆向思维,构造出最后的情况,往上加点、加边,用并查集判断连通块即可

  [ATTENTION]易犯错误:逆向的最开始连通块个数cnt为n-k个而不是n个,每次加点时cnt++,然后在并集的时候再相应地减

 1 #include<cstdio>
 2 #define MAXN 400010
 3 int n,m,k,head[MAXN],fa[MAXN],cnt,ans1,ans2[MAXN];
 4 bool att[MAXN];
 5 struct edge
 6 {
 7     int v,next;
 8 }e[MAXN];
 9 void add(int i,int x,int y)
10 {
11     e[i]=(edge){y,head[x]};
12     head[x]=i;
13 }
14 int getfa(int x)
15 {
16     return fa[x]=fa[x]==x?x:getfa(fa[x]);
17 }
18 void work(int dep)
19 {
20     if(dep==k+1)
21     {
22         for(int u=1;u<=n;u++)
23         {
24             if(att[u])continue;
25             for(int i=head[u];i;i=e[i].next)
26             {
27                 int v=e[i].v;
28                 if(att[v])continue;
29                 int p=getfa(u),q=getfa(v);
30                 if(p!=q)
31                 {
32                     fa[q]=p;
33                     cnt--;
34                 }
35             }
36         }
37         return;
38     }
39     int x;
40     scanf("%d",&x);
41     att[++x]=true;
42     work(dep+1);
43     ans2[++ans1]=cnt;
44     cnt++;
45     att[x]=false;
46     for(int i=head[x];i;i=e[i].next)
47     {
48         int v=e[i].v;
49         if(att[v])continue;
50         int p=getfa(x),q=getfa(v);
51         if(p!=q)
52         {
53             fa[q]=p;
54             cnt--;
55         }
56     }
57 }
58 int main()
59 {
60     scanf("%d%d",&n,&m);
61     int x,y;
62     for(int i=1;i<=m;i++)
63     {
64         scanf("%d%d",&x,&y);
65         add(i<<1,++x,++y);
66         add((i<<1)+1,y,x);
67     }
68     scanf("%d",&k);
69     cnt=n-k;
70     for(int i=1;i<=n;i++)fa[i]=i;
71     work(1);
72     printf("%d\n",cnt);
73     for(int i=ans1;i>=1;i--)printf("%d\n",ans2[i]);
74     return 0;
75 }

PS:感觉自己宛如一个滞胀……竟然在并查集那里卡了好久……

posted @ 2016-10-16 19:56  xqmmcqs  阅读(...)  评论(...编辑  收藏