题意:
你一个字符串,由0和1组成,并且告诉你子串里面1的个数,假设前面的话都是对的,问你到哪一句和前面的话矛盾。
题解:
首先,发现n很大,但是问题数m不多,所以先离散化
d数组表示序列S的前缀和
d[l~r]有偶数个1,等价于d[l-1]与d[r]奇偶性相同。
d[l~r]有奇数个1,等价于d[l-1]与d[r]奇偶性不同。
然后通过异或满足上面传递关系 即 奇偶性相同异或为偶 奇偶性不同异或为奇
集合合并方法与 How Many Answers Are Wrong 类似
d【i】 表示 1到i的和 如果存在 【i,a】与【i,b】 则只要判断 【b,i】^【a-1,i】的奇偶性即可
所以用并查集维护公共点 即根节点 (其实d【i】可以看作根节点到 i 的距离)
那么集合如何合并?
先让fx指向fy 即 f[fy]=fx; 因为 d[fx]^d[x]^d[y]=a[i].ans 所以 d[fx]^d[x]^d[y]=a[i].ans
代码:
#include<iostream> #include<stdio.h> #include<math.h> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int maxn = 2e5+7; vector<int>v; int f[maxn],d[maxn]; int get_id(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;} struct node { int l,r,ans; } a[maxn]; int Find(int x) { if(f[x]==x)return x; int root=Find(f[x]); d[x]^=d[f[x]]; return f[x]=root; } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) { char str[10]; scanf("%d%d%s",&a[i].l,&a[i].r,str); v.push_back(a[i].l-1); v.push_back(a[i].r); a[i].ans=(str[0]=='e'?0:1); } sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end()); for(int i=1; i<=v.size(); i++)f[i]=i,d[i]=0; int res=m; for(int i=1; i<=m; i++) { int x=get_id(a[i].l-1); int y=get_id(a[i].r); int fx=Find(x); int fy=Find(y); if(fx==fy) { if((d[x]^d[y])!=a[i].ans) { res=i-1; break; } } else { f[fx]=fy; d[fx]^=d[x]^d[y]^a[i].ans; /// d[fx]^d[x]^d[y]=a[i].ans } } printf("%d\n",res); return 0; }View Code