• Parity game


    第一行给你一个n(<=10^10)表示有一个长度为n的0,1字符串,
    第二行给你一个m(<=5000)表示接下来有m行输入,
    接下来的m行输入中
    x, y, even表示第x到第y个字符中间1的个数为偶数个,
    x, y, odd表示第x到第y个字符中间1的个数为奇数个,
    若m句话中第k+1是第一次与前面的话矛盾, 输出k;否则输出m

    Sample Input
    10  //长度为10的字符串
    5  //5个询问
    1 2 even  //区间1-2有偶数个1
    3 4 odd  //区间3-4有奇数个1
    5 6 even
    1 6 even
    7 10 odd
    Sample Output
    3  //第4个询问与前3个询问矛盾

    sol:本题用并查集开虚点的方法解决,类似犯罪团伙。

    根据读入提取信息:某区间1的个数的奇偶性,用区间部分和表示。用sum[i]来表示前i个数的和,由于每一位是0、1数字,sum[i]为多少,表示前i位有多少个1。

    若区间[i,j]1的个数为偶数,即sum[j]-sum[i-1]的值为偶数,则说明sum[j]与sum[i-1]的奇偶性一致,因为奇数-奇数得偶数,偶数-偶数得偶数,于是将i,j放在同一个集合。与i,j奇偶性相反的i’与j’应在同一集合。

    若区间[i,j]1的个数为奇数,即sum[j]-sum[i-1]的值为奇数,则说明sum[j]与sum[i-1]的奇偶性相反,因为奇数-偶数得奇数,偶数-奇数得奇数,于是将i,j放在不同的集合。与i奇偶性相反的i’与j应在同一集合,与j奇偶性相反的j’与i应在同一集合。

    另外,本题给出的n的值很大,是10^10,我们做并查集时要记录每一个结点的父亲结点,father数组需要开2*10^10那么大,超内存空间,而我们发现m的值不大,才5000内,就算每个区间的左右端点都不一样,也才10000个点,开虚点再翻倍,并且本题我们在做并查集连边时,只要知道左右端点的关系,是否在同一个集合中,跟两端点具体是什么值无关,因此我们可以做离散化的操作。将每个区间的左右端点映射成较小的数。

    如有3个询问:[2..1000],[3..10^5],[9..10^9],可离散化为[1..4],[2..5],[3..6]

    上面样例具体操作如下:

     

    代码实现如下:

     1 #include <cstdio>  
     2 #include <cstdlib>  
     3 #include <cstring>  
     4 #include <algorithm>  
     5 #include <cmath>  
     6 #include <stack>  
     7 #include <queue>  
     8 #include <vector>  
     9 #include <string>  
    10 #include <climits>  
    11 #include <iostream>  
    12 #define INF 0x3f3f3f3f  
    13 #define ll long long 
    14 #define dd double 
    15 using namespace std;
    16 int dad[20100],m,n,a[20010],t;
    17 char st[20100];
    18 struct node
    19 {
    20     int l,r,ty;
    21 }que[10010];
    22 int getf(int x)
    23 {
    24     return x==dad[x]?x:dad[x]=getf(dad[x]);
    25 }
    26 int main()
    27 {
    28     scanf("%d",&m);
    29     scanf("%d",&n);
    30     for (int i=1;i<=n;i++)
    31     {
    32         scanf("%d%d%s",&que[i].l,&que[i].r,st);
    33         que[i].ty=(st[0]=='o'?1:0);//如果是奇数就TRUE,偶数就FALSE 
    34         a[++t]=que[i].l-1,//记下左边界 
    35         a[++t]=que[i].r;//记下右边界 
    36     }
    37     sort(a+1,a+t+1);//这个是做离散化用的 
    38     m=unique(a+1,a+t+1)-a-1;//算出a数组中有M种不同的数字 
    39     for (int i=1;i<=2*m;i++) //父亲点要开2倍  
    40          dad[i]=i;
    41     for (int i=1;i<=n;i++)//n是询问数 
    42     {
    43         int x=lower_bound(a+1,a+m+1,que[i].l-1)-a;
    44         int y=lower_bound(a+1,a+m+1,que[i].r)-a;
    45         int xx=x+m;//找出各自的对立点 
    46         int yy=y+m;//找出各自的对立点
    47         if (que[i].ty==1)  //如果这个记录是TRUE,则说x,y两个点的奇偶性是不一样的
    48         {
    49             if (getf(x)==getf(y)) //但是它们居然在同一个集合中,说明出错
    50             {
    51                 printf("%d
    ",i-1);
    52                 return 0;
    53             }
    54             dad[getf(x)]=getf(yy);//如果前面没有退出,则将x与yy(y的对立面)放在一个集合中
    55             dad[getf(xx)]=getf(y);//同上
    56         }
    57         else   //说明这个记录是FALSE,则说x,y两个点的奇偶性是一样的
    58         {
    59             if (getf(x)==getf(yy)) //但是它们居然在同一个集合中,说明出错
    60             {
    61                 printf("%d
    ",i-1);
    62                 return 0;
    63             }
    64             dad[getf(x)]=getf(y);//如果前面没有退出,则将x与y放在一个集合中
    65             dad[getf(xx)]=getf(yy);
    66         }
    67     }
    68     printf("%d
    ",n);
    69     return 0;
    70 }

  • 相关阅读:
    蓝桥杯训练 2n皇后问题
    AtCoder Grand Contest #026 B
    AtCoder Grand Contest #026 A
    AtCoder Beginner Contest 103
    7-26 单词长度(15 分)
    uva 10006 Carmichael Numbers
    java并发带返回结果的批量任务执行(CompletionService:Executor + BlockingQueue)
    并发容器之CopyOnWriteArrayList
    模板模式
    静态工具类中使用注解注入service
  • 原文地址:https://www.cnblogs.com/cutepota/p/12557997.html
Copyright © 2020-2023  润新知