题意:
有一个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);
}
}