P5937 [CEOI 1999] Parity Game——前缀和与扩展域并查集

· · 个人记录

题意简述

给定m个要求,存在一个长度为n的序列满足从第1个到第R个要求,求最大的R

初步分析

首先n很大肯定要离散化

如果知道了[L,r][r+1,R]的奇偶性,就可以推出[L,R]的奇偶性

考虑区间dp,但是空间开不下

考虑使用map

挣扎了80pts ## 再次分析 ### 以前缀和的角度思考 若$[l,r]$中1的个数为奇数 则$[1,r]$与$[1,l-1]$的奇偶性不同 偶数同理 这样,我们就**把二维的区间分散到了一维的点上** ### 用并查集维护奇偶性 **奇偶性相同与不同,可以使用扩展域并查集** 可以开一个正域和反域 反域中的元素的奇偶性与正域中是反过来的 也就是说反域中的元素和正域元素的关系都是反过来的 那么,**奇偶性不同的关系也可以由奇偶性相同来维护**了,直接用并查集 ### AC Code: ```cpp #include <bits/stdc++.h> #define even 1 #define odd 2 using namespace std; const int MAXN=5e3+5; struct question{ int l,r,op; }q[MAXN]; int n,m; int lisan[MAXN<<1]; int erf(int x,int l,int r){ int mid; while(l<r){ mid=(l+r+1)>>1; if(lisan[mid]<=x) l=mid; else r=mid-1; } return l; }// int nw; void discretize(){ for(int i=1;i<=m;++i){ lisan[i] =q[i].l; lisan[i+m] =q[i].r;; } sort(lisan+1,lisan+1+m*2); nw=unique(lisan+1,lisan+1+m*2)-lisan-1; for(int i=1;i<=m;++i){ q[i].l=erf(q[i].l,1,nw); q[i].r=erf(q[i].r,1,nw); } }// int fa[MAXN<<2];//扩展域并查集,正域和反域 //维护的关系是奇偶性相同 void init(){ for(int i=0;i<=m*4;++i){ fa[i]=i; } }// int find(int x){ while(x!=fa[x]) x=fa[x]=fa[fa[x]]; return x; }// void merge(int x,int y){ x=find(x),y=find(y); fa[x]=y; }// string s; void work(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ scanf("%d%d",&q[i].l,&q[i].r); q[i].l--; cin>>s; if(s=="odd") q[i].op=odd; else q[i].op=even; } discretize(); init(); for(int i=1,l,r;i<=m;++i){ l=q[i].l;r=q[i].r; if(q[i].op==even){//偶数的话奇偶性相同 if(find(l)==find(r+m*2)&&find(l+m*2)==find(r)){ printf("%d",i-1); return ; } merge(l,r); merge(l+m*2,r+m*2); }else{ if(find(l)==find(r)&&find(l+m*2)==find(r+m*2)){ printf("%d",i-1); return ; } merge(l,r+m*2); merge(l+m*2,r); } } printf("%d",m); }// int main(){ work(); return 0; } ```