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;
    }
    
  • 相关阅读:
    nginx error_log报错upstream timed out (110: Connection timed out)
    ubuntu12.04 nginx添加ssl认证
    TP5.0:同一个控制器访问不同方法
    TP5.0:的安装与配置
    html常用的小技能
    详情介绍win7:编辑文件夹时提示操作无法完成,因为其中的文件夹或文件已在另一个程序中打开的解决过程
    详细讲解:通过phpstudy 设置站点域名、站点域名管理
    tp3.2.3自定义全局函数的使用
    详细讲解:tp3.2.3生成验证码并进行验证(ajax校验返回及自定义返回)
    WPS去掉英语单词下面的红斜线
  • 原文地址:https://www.cnblogs.com/streamazure/p/13466932.html
Copyright © 2011-2022 走看看