CF1386C Joker 双指针+动态树
双指针+LCT.
在二分图那道题中,有一种用 LCT 维护奇环的方法.
该题的做法和那道题相似.
对于 $i$,维护 $left[i]$ 表示当删除 $i$ 时最靠左的端点使得删掉 $[left[i]+1,i]$ 后仍然存在奇环.
那么最后判断答案的时候就看 $(l,r)$ 中的 $l$ 是否大于 $left[r]$ 即可.
由于删掉边一定不会使答案更优,所以 $i$ 增加时 $left[i]$ 不会减小,故 $[left[i],i]$ 满足双指针性质(即不存在包含的情况)
将边分为两类:
1. 由于 $i$ 变大被删掉的.
2. 由于双指针中左端点的移动被加入的.
显然对于 $1$ 类边希望出现时间越晚越好,对于 $2$ 类边没有要求.
根据上述结论,我们在预处理边的时候贪心加入即可(即加不进去就不加)
然后双指针移动的时候去替换结束时间最小的边,类似最大生成树.
#include <cstdio> #include <cstring> #include <algorithm> #define N 400008 #define ll long long #define ls s[x].ch[0] #define rs s[x].ch[1] #define setIO(s) freopen(s".in","r",stdin) using namespace std; const int inf=100000000; int det[N]; int left[N],sta[N<<1],vis[N<<1],n,m,Q; struct Edge { int u,v,t; Edge(int u=0,int v=0,int t=0):u(u),v(v),t(t){} }a[N],arr[N]; struct data { int ch[2],rev,f,v,id,si,t; }s[N<<2]; inline int get(int x) { return s[s[x].f].ch[1]==x; } inline int isr(int x) { return s[s[x].f].ch[0]!=x&&s[s[x].f].ch[1]!=x; } void pushup(int x) { s[x].id=x; s[x].si=(x<=n)+s[ls].si+s[rs].si; if(ls&&s[s[ls].id].t<s[s[x].id].t) { s[x].id=s[ls].id; } if(rs&&s[s[rs].id].t<s[s[x].id].t) { s[x].id=s[rs].id; } } void mark(int x) { s[x].rev^=1,swap(ls,rs); } void pushdown(int x) { if(s[x].rev) { if(ls) mark(ls); if(rs) mark(rs); s[x].rev=0; } } void rotate(int x) { int old=s[x].f,fold=s[old].f,which=get(x); if(!isr(old)) { s[fold].ch[s[fold].ch[1]==old]=x; } s[old].ch[which]=s[x].ch[which^1]; if(s[old].ch[which]) s[s[old].ch[which]].f=old; s[x].ch[which^1]=old,s[old].f=x,s[x].f=fold; pushup(old),pushup(x); } void splay(int x) { int u=x,v=0,fa; for(sta[++v]=u;!isr(u);u=s[u].f) { sta[++v]=s[u].f; } for(;v;--v) pushdown(sta[v]); for(u=s[u].f;(fa=s[x].f)!=u;rotate(x)) if(s[fa].f!=u) { rotate(get(fa)==get(x)?fa:x); } } void access(int x) { for(int y=0;x;y=x,x=s[x].f) { splay(x),rs=y,pushup(x); } } void makert(int x) { access(x),splay(x),mark(x); } void split(int x,int y) { makert(x),access(y),splay(y); } int find(int x) { access(x),splay(x); while(ls) pushdown(x),x=ls; splay(x); return x; } void ADD(int x,int y) { makert(x),s[x].f=y; } void DEC(int x,int y) { makert(x),access(y),splay(y); s[y].ch[0]=s[x].f=0; pushup(y); } void exi(int lst) { if(lst==0) { for(int i=1;i<=Q;++i) { int x,y; scanf("%d%d",&x,&y); printf("NO\n"); } exit(0); } } int main() { // setIO("input"); int x,y,z; scanf("%d%d%d",&n,&m,&Q); for(int i=1;i<=m;++i) { scanf("%d%d",&x,&y); arr[i]=Edge(x,y,i); } int lst=0; a[0].t=inf; for(int i=1;i<=n;++i) { s[i].t=inf; s[i].id=i; } for(int i=m;i>=1;--i) { x=arr[i].u; y=arr[i].v; a[i]=arr[i]; if(find(x)==find(y)) { split(x,y); if(s[y].si&1) { lst=i; break; } } else { int now=n+i; s[now].v=i; s[now].t=i; pushup(now); vis[i]=1; ADD(x,now),ADD(y,now); } } int tot=n+m; exi(lst); int p=1,cnt=0; for(int i=lst;i<=m;++i) { int flag=0; for(cnt+=det[i];!cnt&&p<=i;++p) { a[m+p]=arr[p]; x=arr[p].u,y=arr[p].v; if(find(x)==find(y)) { split(x,y); int pr=s[y].id; int nx=s[pr].v; if(s[y].si&1) { ++cnt,--det[a[nx].t]; } if(a[nx].t<=m) { vis[s[y].id-n]=0; } DEC(pr,a[nx].u); DEC(pr,a[nx].v); } int now=++tot; a[m+p].t=m+1; s[now].v=m+p; s[now].t=m+1; pushup(now),ADD(now,x),ADD(now,y); } left[i]=p-1; if(i+1<=m&&vis[i+1]) { vis[i+1]=0; int now=i+1+n; DEC(now,arr[i+1].u); DEC(now,arr[i+1].v); } } for(int i=1;i<=Q;++i) { scanf("%d%d",&x,&y); if(y<lst) { printf("YES\n"); } else { if(x>left[y]) printf("YES\n"); else printf("NO\n"); } } return 0; }