种类并查集

Zjnu Stadium HDU - 3047 

逾期说是种类并查集,其实更像是权值并查集,以一个点作为基准,通过路径压缩把所有的座位关系都变成与基准有直接关系的,查询的过程也是不断维护集合的过程,没有当前查询的信息,就插入查询的信息,如果有,则判断是否与集合中的信息冲突。

对于每次加入的时候都有下面这个关系,根据这个等价关系,求出cnt[fb],然后通过find函数进行路径压缩和更新,这大致是权值并查集的通常做法。无论这个权值是区间和或者是奇偶分析等,只要能通过这个关系更新的就可以做。

 1 #include <iostream>
 2 using namespace std;
 3 const int maxn=1e5+5;
 4 int n,m;
 5 int f[maxn],cnt[maxn];
 6 void init()
 7 {
 8     for(int i=0;i<=n;i++)
 9         f[i]=i,cnt[i]=0;
10 }
11 int find(int x){
12     if(x!=f[x]){
13         int fa=f[x];
14         f[x]=find(f[x]);
15         cnt[x]=(cnt[x]+cnt[fa])%300;
16     }
17     return f[x];
18 }
19 int main()
20 {
21     std::ios::sync_with_stdio(false);
22     while(cin>>n>>m)
23     {
24         init();long long  ans=0;
25         for(int i=1;i<=m;i++){
26             //cout <<i<<" "<<m<<endl;
27             int a;int b;int c;
28             cin>>a>>b>>c;
29             int fa=find(a);
30             int fb=find(b);
31             
32             if(fa!=fb){
33                 f[fb]=fa;
34                 cnt[fb]=(c+cnt[a]-cnt[b])%300;
35             }else
36             {
37                 if((cnt[b]-cnt[a]+300)%300!=c%300) ans++;
38             }
39         }
40             cout <<ans<<endl;
41     }
42     return 0;
43  } 
View Code

B - 食物链 POJ - 1182 

之前被这道题支配了好久,做完上面那道题后,发现这不是一模一样嘛,通过被吃关系或吃关系建立一个圈,那就和上面的操场那个完全一样了,只是这个有两种操作。

我们回顾并查集,最初的并查集是作为种类分类的集合,但是到后来的权值并查集显然就不是这个作用了,它通过以根为基准建立了一套偏移系,通过路径压缩,能够把每个元素的偏移直接与根连接上,查询就很容易计算。不在这个集合中的就先加进来。种类并查集只是有一个mod系的权值并查集,而且还比较好写。这个题用cin关同步T了,快读500ms,scanf200ms。

ps:这些东西一旦想明白了很简单,想不明白是真的很自闭。

附代码:

 1 #include <iostream>
 2 using namespace std;
 3 const int maxn=1e5+5;
 4 int n,m;
 5 int f[maxn],cnt[maxn];
 6 void init()
 7 {
 8     for(int i=0;i<=n;i++)
 9         f[i]=i,cnt[i]=0;
10 }
11 int find(int x){
12     if(x!=f[x]){
13         int fa=f[x];
14         f[x]=find(f[x]);
15         cnt[x]=(cnt[x]+cnt[fa])%300;
16     }
17     return f[x];
18 }
19 int main()
20 {
21     std::ios::sync_with_stdio(false);
22     while(cin>>n>>m)
23     {
24         init();long long  ans=0;
25         for(int i=1;i<=m;i++){
26             //cout <<i<<" "<<m<<endl;
27             int a;int b;int c;
28             cin>>a>>b>>c;
29             int fa=find(a);
30             int fb=find(b);
31             
32             if(fa!=fb){
33                 f[fb]=fa;
34                 cnt[fb]=(c+cnt[a]-cnt[b])%300;
35             }else
36             {
37                 if((cnt[b]-cnt[a]+300)%300!=c%300) ans++;
38             }
39         }
40             cout <<ans<<endl;
41     }
42     return 0;
43  } 
View Code

 C - True Liars POJ - 1417 

这个题卡了巨长时间。最后玄学的把C++换成G++过了。

模二系种类并查集+方法01dp。先找出每个集合,因为这几个集合无法统一基准,所以要选择哪种组合可以满足题目,01背包算出用这cnt个集合凑p1个人方法数。然后反向推回去,记录每一步的选择情况。实现起来比较困难。需要找好一一对应的关系

  1 #include <iostream>
  2 #include <cstring>
  3 #include <map>
  4 #define deg(x)  cout <<"x= "<<x<<endl
  5 using namespace std;
  6 
  7 int n,p1,p2;
  8 int f[1000],cnt[1000];
  9 struct node{
 10     int num1;
 11     int num0;
 12 }bag[1000];
 13 int dp[1000][1000];
 14 void init(){
 15     memset(bag,0,sizeof(bag));
 16     memset(dp,0,sizeof(dp));
 17     for(int i=0;i<=p1+p2;i++){
 18         cnt[i]=0;f[i]=i;
 19     }
 20 }
 21 
 22 int find(int x)
 23 {
 24     
 25     if(x!=f[x])
 26     {
 27         int fa=f[x];
 28         f[x]=find(f[x]);
 29         cnt[x]=(cnt[x]+cnt[fa])%2;
 30     }
 31     return f[x];
 32 }
 33 
 34 bool read()
 35 {
 36     
 37     char s[5];int a,b;
 38     int sign=1;
 39     /*for(int i=1;i<=p1+p2;i++)
 40     {
 41         cout <<f[i]<<" ";
 42     }*/
 43     //cout <<endl;
 44     //std::ios::sync_with_stdio(false);
 45     for(int i=1;i<=n;i++)
 46     {
 47         cin>>a>>b>>s;
 48         int fa=find(a);int fb=find(b);
 49         if(s[0]=='y')
 50         {
 51             if(fa!=fb)
 52             {
 53                 f[fb]=fa;
 54                 cnt[fb]=(cnt[a]-cnt[b]+2)%2;
 55             }else
 56             {
 57                 if(cnt[b]%2!=cnt[a]%2)
 58                     sign=0;
 59                 //cout <<"in"<<i<<endl;
 60             }
 61 
 62         }else
 63         {
 64             if(fa!=fb)
 65             {
 66                 f[fb]=fa;
 67                 cnt[fb]=(cnt[a]+1-cnt[b]+2)%2;
 68              } else
 69              {
 70                  //cout <<"in"<<i<<endl;
 71                  if((cnt[a]+1)%2!=cnt[b]%2)
 72                      sign=0;
 73              }
 74         }
 75     //    cout <<sign<<"sing"<<endl;
 76         
 77     }
 78     return sign;
 79 }
 80 
 81 int main()
 82 {
 83     //freopen("a.txt","r",stdin);
 84     //std::ios::sync_with_stdio(false);
 85     while(cin>>n>>p1>>p2)
 86     {
 87         if(n==0&&p1==0&&p2==0) break;
 88         init();int sign=read();
 89     /*    for(int i=1;i<=p1+p2;i++)
 90         {
 91             cout <<i<<" "<<f[i]<<" "<<cnt[i]<<endl;
 92         }
 93         cout <<"sign="<<sign<<endl;*/
 94         if(sign==0) cout <<"no\n";
 95         else
 96         {
 97             map <int,int> M;
 98             for(int i=1;i<=p1+p2;i++)
 99             {
100                 int fa=find(i);
101                 if(cnt[i]==1) bag[fa].num1++;
102                     else bag[fa].num0++;
103             }
104             int ct=0;
105             for(int i=1;i<=p1+p2;i++){
106                 //deg(bag[i].num0);
107                 //deg(bag[i].num1);
108             }
109             for(int i=1;i<=p1+p2;i++)
110             {
111                 if(bag[i].num0==0&&bag[i].num1==0)    continue;
112                 ct++;
113                 bag[ct].num0=bag[i].num0;bag[ct].num1=bag[i].num1;
114                 M[i]=ct;//根对应背包编号 
115             }
116         /*    for(int i=1;i<=ct;i++)
117             {
118                 cout <<bag[i].num0<<" "<<bag[i].num1<<endl;
119             }*/
120             dp[0][0]=1;
121             for(int i=1;i<=ct;i++)
122             {
123                 for(int j=0;j<=p1;j++)
124                 {
125                     if(j>=bag[i].num0) dp[i][j]=dp[i-1][j-bag[i].num0];
126                     if(j>=bag[i].num1) dp[i][j]+=dp[i-1][j-bag[i].num1];
127                 }
128             }
129             if(dp[ct][p1]!=1) {
130                 cout <<"no\n";
131                 continue;
132             }
133             int choose[1000]={0};int y=p1;
134             for(int i=ct;i>=1;i--){
135                 //cout <<"tia"<<dp[i-1][y-bag[i].num0]<<" "<<dp[i-1][y-bag[i].num1]<<endl;
136                 if(dp[i][y]==dp[i-1][y-bag[i].num0])
137                 {
138                      choose[i]=0;y=y-bag[i].num0;
139                 }
140                 else if(dp[i][y]==dp[i-1][y-bag[i].num1]) 
141                 {
142                     choose[i]=1;y=y-bag[i].num1;
143                 }
144             }
145             /*for(int i=1;i<=ct;i++)
146             {
147                 cout <<choose[i]<<endl;
148             }*/
149             for(int i=1;i<=p1+p2;i++){
150                 int fa=find(i);
151                 int z=M[fa];
152                 if(choose[z]==cnt[i])
153                     cout <<i<<'\n'; 
154             }
155             cout <<"end\n";
156         }
157         
158     }
159     return 0;
160 }
View Code

 

 

 

 

 

转载于:https://www.cnblogs.com/Msmw/p/11257264.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值