zoukankan      html  css  js  c++  java
  • 【带权并查集+离散化】Parity game POJ

    Parity game POJ - 1733

    题意:

    有一个长度已知的01串,给出一系列包含(l,r)的语句,表示([l,r])这个区间中的1是奇数个还是偶数个。问前多少条语句是正确的。

    思路:

    一:集合元素的选定

    首先为了能连通,需要把所给闭区间改为左开右闭区间,如第2~4位上有偶数个1,改写为((1,4])上有偶数个1。那么每一个左端点都可视为集合元素,其父节点为右端点。每个结点(i)与其根节点(root[i])的关系(rela[i]),即区间((i,root[i]))上1的个数的奇偶性。

    对于给出的闭区间(u - v),结点编号应该是(u-1)(v)

    注意要先(-1)之后再离散化。

    const int maxn=1e4+10;
    int fa[maxn];
    int rela[maxn];
    //0表示偶数个,1表示奇数个
    
    二:离散化

    由于结点编号可能大至(1e9),如果不做处理,将编号直接作为数组下标显然不可行(如果一个结点的编号是(1e9),意味着数组大小要开到(1e9)才能存这个结点)。又因为(n<=5000),也即结点数最多只有(10000)个,可以将所给编号离散化至(10000)以内的数。

    //用map将原编号离散化,并将返回新编号
    int Hash(int x){
    	if(mp.find(x)==mp.end()) mp[x]=++pos;
    	return mp[x];
    }
    
    三:压缩路径时的关系维护

    若结点(B)是结点(A)的父节点,已知:(A)(B)的关系(rela[A])(B)与根节点的关系(rela[B]),求(A)与根节点的关系。

    这里的关系即两个结点之间1的个数的奇偶性,因此所有情况如下表所示:

    rela[A] rela[B] A与根节点的关系
    0 0 0
    0 1 1
    1 0 1
    1 1 0

    可以看出,这是异或运算。

    int find(int A){
          if(fa[A] == -1) return A;
          //无父节点,说明A已是最右边的端点
          int root_B = find(fa[A]);
          //B即fa[A]
          rela[A] = rela[A] ^ rela[fa[A]];
          //更新结点A与根节点的关系
          return fa[A] = root_B;
          //更新关系后,再将A与根节点连接
    }
    
    四:合并集合时的关系维护

    已知:区间((u,fa[u] ])上的关系(rela[u])、区间((v,fa[v]])上的关系(rela[v]),现又知道区间((u,v])上的关系(r),如何合并集合?

    (务必注意这里的(fa[u])(fa[v])都是路径压缩后更新过的,是根节点而不是父节点。)

    合并集合,实际上是需要求两个根节点(fa[u])(fa[v])之间的关系(rela[fa[u]])

    举个例子:已知区间((0,3])(1),区间((6,8])(0),现又已知区间((0,6])(1),求区间((3,8])。结果是0。

    所有情况如下表所示:

    rela[u] rela[v] r fa[u]与fa[v]的关系
    0 0 1 1
    0 1 0 1
    0 1 1 0
    1 0 0 1
    1 0 1 0
    1 1 0 0
    1 1 1 1

    由此可见,(fa[u])(fa[v])之间的关系(rela[fa[u]]=rela[u]) ^ (rela[v]) ^ (r)

    int r1 = find(u);
    int r2 = find(v);
    //路径压缩,更新u,v根节点
    if (r1 != r2) {
    	//若u,v不在同一集合中
    	fa[r1] = r2;
    	rela[r1] = rela[u] ^ rela[v] ^ r;
    }
    
    五:关系查询

    这一步是判断给出的关系是否符合由先前条件所推出的关系。只有当两结点在同一集合中时,才能确保两结点间的关系可以推出。

    判断是否在同一集合中,即判断两结点的根节点是否相同。

    if (r1 == r2){
    	int relation = rela[u] ^ rela[v];
    	//先推出u,v的关系
    	return relation == r;
    	//r为给出的关系
    }
    
    完整代码:

    (这里是先把所有给出的关系先存起来离线处理,如果在线处理的话代码会更简洁一些)

    const int maxn = 1e4+10;
    int fa[maxn],rela[maxn];
    int x[maxn],y[maxn],r[maxn];
    int k, n, pos;
    map<int, int> mp;
    
    //编号离散化
    int Hash(int x){
          if(mp.find(x)==mp.end()) mp[x]=++pos;
          return mp[x];
    }
    
    //路径压缩
    int find(int x) {
        if (fa[x] == -1) return x;//没有右端点了
        int tmp = find(fa[x]);
        //先找到根
        rela[x] = rela[fa[x]] ^ rela[x];
        return fa[x] = tmp;
        //!注意:维护关系之后再更新x的根节点
    }
    
    //集合合并+查询
    bool merge(int u,int v,int rr){
      int r1 = find(u);
      int r2 = find(v);
      if(r1 != r2){
        fa[r1] = r2;
        rela[r1] = rela[u] ^ rela[v] ^ rr;
        return true;
        //不在同一集合,关系无法由已知推出,那么给出的关系一定是正确的
      }
      else{
        int relation = rela[u] ^ rela[v];
        return relation == rr;
      }
    }
    
    int main()
    {
        //ios::sync_with_stdio(false);
        memset(fa, -1, sizeof(fa));
        memset(rela, 0, sizeof(rela));
        //初始化为0!不能是-1!
        int pos = 0;
        int ans = 0;
        char str[20];
        cin >> k >> n;
        for (int i = 1; i <= n; i++) {
            int tx, ty;
            cin >> tx >> ty;
            cin >> str;
            x[i] = Hash(tx - 1);
            //先取左端点的开区间再离散化
            y[i] = Hash(ty);
            if (str[0] == 'e') r[i] = 0;
            else r[i] = 1;
        }
    
        for (int i = 1; i <= n; i++) {
            int a = x[i], b = y[i];
            if (a > b) swap(a, b);
    	if (merge(a,b,r[i])) ans++;
            //关系正确,答案+1
            else break;
            //关系错误,直接跳出
        }
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    HDU 2236 无题Ⅱ
    Golden Tiger Claw(二分图)
    HDU 5969 最大的位或 (思维,贪心)
    HDU 3686 Traffic Real Time Query System (图论)
    SCOI 2016 萌萌哒
    Spring Boot支持控制台Banner定制
    构建第一个Spring Boot程序
    Spring Boot重要模块
    Java fastjson JSON和String互相转换
    BCompare 4 Windows激活方法【试用期30天重置】
  • 原文地址:https://www.cnblogs.com/streamazure/p/13466932.html
Copyright © 2011-2022 走看看