ural 1003. Parity(并查集)

96 篇文章 0 订阅
64 篇文章 0 订阅

题意:

有一个01序列,现在有n个声明,每个问题,每个问题包含两个数字l,r,已经一个字符串表示odd和even,问题的含义是让这个序列l到r之间的1的个数为even和odd,现在问你能构造出一个序列最多满足前多少个问题。


解题思路:

这道题的转换还是有点巧妙的。

假装这里有一个01序列,要知道l,r内有多少个1的话,最好的办法就是先预处理一个前缀和,查询一下也就可以,因为是问是奇数个还是偶数个,那么只要求前缀和pre[r]-pre[l-1]的奇偶就行了,两个数相减得到一个奇数的话就要求这两个数奇偶性不同,要求得到一个偶数的话就要求这两个数的奇偶性相同,那么数与数之间的关系是不是很像敌对和朋友关系呢?到了这里就可以明显看出来用并查集了。但是处理敌对关系的时候,我们需要虚拟一个敌人,首先对每个数x虚拟一个敌人为x+len,len就是总格数的个数,然后对于每个为even的两个数,就要求l-1不能与r+len同一个祖先,r不能与l-1+len同一个祖先,满足的话就把l-1和r合并,l-1+len和r+len合并,odd类推,这样就处理出来两个阵营了。

然后需要对数离散化一下。

一直不知道错在哪里,重写一遍就过了。。

代码:

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int l;
    int r;
    int same;
}que[5005];
int num[10005];
int f[10005];
int getf(int x)
{
    if(f[x]==x)return x;
    else return f[x]=getf(f[x]);
}

int main()
{
    int m;
    while(~scanf("%d", &m))
    {
        if(m==-1)break;
        int n, i, j;
        scanf("%d", &n);
        char str[12];
        for(i=1, j=0; i<=n; i++)
        {
            scanf("%d%d%s", &que[i].l, &que[i].r, str);         
            if(que[i].l>que[i].r)swap(que[i].l, que[i].r);
            que[i].l--;
            que[i].same=(str[0]=='e');
            num[j++]=que[i].l, num[j++]=que[i].r;
        }
        sort(num, num+j);
        int len=unique(num, num+j)-num;
        int x, y;
//        for(i=0; i<len; i++)printf("%d ", num[i]);printf("\n");
        for(i=0; i<=len*2+10; i++)f[i]=i;
        for(i=1; i<=n; i++)
        {
            x=que[i].l, y=que[i].r;
            x=lower_bound(num, num+len, x)-num;
            y=lower_bound(num, num+len, y)-num;
//            printf("%d %d\n", x, y);
            int emx, emy;
            emx=x+len;
            emy=y+len;
            x=getf(x), y=getf(y), emx=getf(emx), emy=getf(emy);
            if(que[i].same)
            {
                if(x==emy || y==emx)
                {
                    break;
                }               
                else 
                {
                    f[x]=y, f[emx]=emy;
                }
            }
            else 
            {
                if(x==y || emx==emy)
                {
                    break;
                }
                else 
                {
                    f[x]=emy, f[y]=emx;
                }
            }
        }
        printf("%d\n", i-1);
        
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值