分组[测试点分支+二分图判定]

题意大概是给你一个序列,让你把这个序列划分成几个连续的小组,每个小组又可以分成两份,要求使所分小组数最小,且满足每个小组内部的每个小团体都不能有任意两个数相加为

整数的平方......

1.首先一个很重要的点,数据范围是1<<17,2*(1<<17)=1<<18;而sqrt(1<<18)=1<<9=512;所以枚举判断是否合法,即可只枚举512次而避免了O(N^2)复杂度;

2.要求字典序最小的答案,而咱们又要贪心,所以从后往前转移.....

3.其实依题,判断每个小组是否合法,因为每个小组内只有且仅有2个小团体,所以,就可以是看成是二分图判断问题,如果判断可以构成二分图,就继续往下搜;

我们先来回忆一下二分图;

二分图的定义是:把一组点分成两组,且每组内部的点没有连边联系,而两组之间可以有连边;

这样考虑的话

我们可以把每个数与其敌对数(当然是同组内的)先建边,这样建成的边像(关押罪犯)一样可以是看成仇恨边,而在这道题里,有仇恨的数不能放在同一个集合里,,同一集合的

数是不可有仇恨连边的,而两组之间可以有仇恨边,仔细读一下,和二分图定义是一样的;

好我们就可以把这个题转化成二分图判断题;

而从后枚举,就是让你目前的二分图尽可能长,所以就是去每次添加一个点,然后把这个点加入二分图,看是否仍能符合二分图的性质;

然后就到了如何去判断二分图;

一.交叉染色法判断二分图

 1 bool judge=0;
 2 void dfs(int x,int fa,int cols){
 3       col[x]=cols;
 4       if(judge)return ;
 5     for(int i=fir[x],y;i;i=edge[i].nxt){
 6           y=edge[i].to;
 7           if(judge)return ;
 8           if(y==fa)continue;
 9           if(cols+col[y]==3)continue;//颜色编号为1和二
10           if(cols==col[y])return judge=1,void();
11           if(!col[y])dfs(y,x,3-cols);
12     }    
13 }
14 //其实就是你和与你连边的点是敌对的,你不能和你敌对的点有相同的颜色即同一集合,又因为//和你连边的都是对立点,所以你就把和你连边的点染成异色,只要不是和你对立的点还和你有//相同的颜色(在同一个集合)就可以了;
交叉染色法

二.并查集牛逼法(弓主任(交线牛逼法))

 1 #define MAXN 100050
 2 #define mat(x) (x+N)
 3 //虚点~
 4 inline int find(int x){
 5     return (x==fa[x])?x:fa[x]=find(fa[x]);
 6 }
 7 int fa[MAXN],N;
 8 vector<int>ans;
 9 int main()
10 {
11     int M=(N<<1);
12     for(int i=1;i<=M;i+=5){
13         fa[i]=i;
14         if(i+1<=M)fa[i+1]=i+1;
15         if(i+2<=M)fa[i+2]=i+2;
16         if(i+3<=M)fa[i+3]=i+3;//卡常一下
17         if(i+4<=M)fa[i+4]=i+4;
18     }//并查集要用到拓展域和虚点
19     //虚点的作用是为了让与和你对立的点不再和你连边而和你的虚点连边
20     //而你的虚点就可以设为(i+N)
21     bool judge=0;
22     for(int i=N;i>=1;--i){
23         judge=0;
24         for(int j=0,y;j<to[i].size();++j){
25             y=to[i][j];
26             fa[find(i)]=fa[find(mat(y)];
27             fa[find(y)]=fa[find(mat(i))];
28             if(fa[i]==fa[y]){//其实也一样,就是你把和你敌对的点连在了你的虚点上,
29                         //然后你的虚点不能和你连便,因为你不能和你自己敌对
30                         //用并查集维护就是可以加快你判断与你对立的点是否和你在同一个集合里
31                         //用交叉染色法判一次O(N),他判一次差不多O(1);
32                         // 所以还是这个比较好    
33                 judge=1;break;
34             }    
35         }
36         if(judge){
37             //清零数组一大堆懒得写了反正是思路;
38             ans.push_back(i);
39         }
40     }
41     
42 }
并查集判断二分图

差不多这个题最重要的就是判断二分图;

模板好题提醒(关押罪犯)应该先做;

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 int N,K;
  4 int col[331079],fir[331079];
  5 bool v[331079];
  6 int h[331707];
  7 int c[331079];
  8 vector<int>ans;
  9 int has[331079];
 10 vector<vector<int> >to(331079),num(331107);
 11 struct ct{int st,ed,nxt;}lian[10011007];int tot=0;
 12 
 13 inline int max(int a,int b){return a>b?a:b;}
 14 
 15 inline void add(int x,int y){
 16     lian[++tot].st=x;
 17     lian[tot].ed=y;
 18     lian[tot].nxt=fir[x];
 19     fir[x]=tot;
 20 }
 21 int maxn=0;
 22 bool judge=0;
 23 void dfs(int x,int fa,int cs){
 24     c[x]=cs;register int y;
 25     if(judge)return ;
 26     for(int i=fir[x];i;i=lian[i].nxt){
 27         y=lian[i].ed;
 28         if(judge)return ;
 29         if(!v[y])continue;
 30         if(y==fa)continue;
 31         if(c[y]+cs==3)continue;
 32         if(c[y]==cs)return judge=1,void();
 33         if(!c[y])dfs(y,x,3-cs);
 34     }
 35 }
 36 
 37 int main()
 38 {
 39     scanf("%d%d",&N,&K);
 40     for(int i=1;i<=N;++i){
 41         scanf("%d",&col[i]);
 42     }
 43     int lim=512;
 44    if(K==1){ for(int i=N;i>=1;--i){
 45         for(int j=lim;j*j>=col[i];--j){
 46             if(v[j*j-col[i]]){
 47                 for(int k=1;k<=has[0];++k)
 48                     v[has[k]]=0;
 49                 has[0]=0;ans.push_back(i);
 50                 break;
 51             }
 52         }
 53         v[col[i]]=1;has[++has[0]]=col[i];
 54         }
 55     }
 56 
 57    else
 58     {
 59     register int deta=1<<17;
 60         for(int i=N;i>=1;--i){
 61             judge=0;
 62             lim=sqrt(maxn+col[i])+1;
 63             for(int j=lim;j*j>=col[i];--j)
 64             if(j*j-col[i]<=(deta)){
 65                     for(int k=0;k<h[j*j-col[i]];++k){
 66                         register int p=num[j*j-col[i]][k];
 67                         if(p!=i){
 68                             add(p,i);
 69                             add(i,p);
 70                         }
 71                     }
 72             }
 73             v[i]=1;
 74             dfs(i,i,1);
 75             //for(int is=0;is<=131072;++is)c[is]=0;
 76             if(judge){
 77                 has[++has[0]]=i;
 78                 ans.push_back(i);//c[i]=0;
 79                 for(register int j=1;j<=has[0];j+=4){
 80                 v[has[j]]=0,c[has[j]]=0,fir[has[j]]=0,h[col[j]]=0,num[col[j]].clear();
 81                 if(j+1<=has[0]){v[has[j+1]]=0,c[has[j+1]]=0,fir[has[j+1]]=0,h[col[j+1]]=0,num[col[j+1]].clear();}
 82                 if(j+2<=has[0]){v[has[j+2]]=0,c[has[j+2]]=0,fir[has[j+2]]=0,h[col[j+2]]=0,num[col[j+2]].clear();}
 83                 if(j+3<=has[0]){v[has[j+3]]=0,c[has[j+3]]=0,fir[has[j+3]]=0,h[col[j+3]]=0,num[col[j+3]].clear();}
 84                 }
 85                 tot=0;
 86                 has[0]=0;
 87                 maxn=col[i];
 88             }
 89             v[i]=1;
 90             has[++has[0]]=i;
 91             h[col[i]]++;
 92             maxn=max(maxn,col[i]);
 93             num[col[i]].push_back(i);
 94             for(register int j=1;j<=has[0];j+=5){
 95             c[has[j]]=0;
 96             if(j+1<=has[0])c[has[j+1]]=0;
 97             if(j+2<=has[0])c[has[j+2]]=0;
 98             if(j+3<=has[0])c[has[j+3]]=0;
 99             if(j+4<=has[0])c[has[j+4]]=0;
100             }
101         }
102        }
103        printf("%d\n",(ans.size())+1);
104        for(int i=ans.size()-1;i>=0;--i){
105            printf("%d ",ans[i]);
106         }
107         printf("\n");
108 }
因卡常恰巧AC的代码

 

posted @ 2019-08-04 15:09  Hzoi_whs  阅读(255)  评论(0编辑  收藏  举报