乱搞好题。。。
考虑加入一条边,如果生成环,那么把环上最早加入的边删去,答案就是用n-(编号在l到r的边中删去的边的编号在1到l中的边)。证明的话,yy一下吧。
想到这你可以激动的去写一发树上倍增,突然发现无法处理边被替换
怎么办?上LCT。
作为一名(并不)熟练的NOIP选手,LCT+主席树这种东西要在1小时1天内写完。
代码:
/**************************************************************
Problem: 3514
User: sckalrter
Language: C++
Result: Accepted
Time:36228 ms
Memory:64184 kb
****************************************************************/
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=4e5+5;
int read(){
int x=0,f=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int ch[N][2],fa[N],mn[N],val[N],st[N],n,m,k,tp,s[N];
struct Edg{
int pre,poi;
}e[N];
struct Tre{
int l,r,a;
}tree[N*10];
int root[N],rt=0;
bool rev[N];
bool isroot(int x){
return (ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x);
}
void update(int x){
int l=ch[x][0],r=ch[x][1];
mn[x]=x;
if (val[mn[l]]<val[mn[x]]) mn[x]=mn[l];
if (val[mn[r]]<val[mn[x]]) mn[x]=mn[r];
}
void down(int x){
int l=ch[x][0],r=ch[x][1];
if (rev[x]){
rev[l]^=1; rev[r]^=1; rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l,r;
if (ch[y][0]==x) l=0; else l=1; r=l^1;
if (!isroot(y)){
if (ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;
}
fa[x]=z; fa[y]=x; fa[ch[x][r]]=y;
ch[y][l]=ch[x][r]; ch[x][r]=y;
update(y); update(x);
}
void splay(int x){
int tot=0; st[++tot]=x;
int y=x;
for (int i=x;!isroot(i);i=fa[i])
st[++tot]=fa[i];
for (int i=tot;i>=1;i--) down(st[i]);
while (!isroot(x)){
int y=fa[x],z=fa[fa[x]];
if (!isroot(y)){
if (ch[y][0]==x^ch[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
for (int y=0;x;y=x,x=fa[x]){
splay(x); ch[x][1]=y; update(x);
}
}
void makeroot(int x){
access(x); splay(x); rev[x]^=1;
}
void link(int x,int y){
makeroot(x); fa[x]=y;
}
int findroot(int x){
access(x); splay(x);
while (ch[x][0]) x=ch[x][0];
return x;
}
void cut(int x,int y){
makeroot(x); access(y);
splay(y); fa[x]=ch[y][0]=0;
}
int query(int x,int y){
makeroot(x); access(y); splay(y);
return mn[y];
}
void insert(int &u,int v,int l,int r,int x){
u=++rt;
tree[u]=tree[v];
tree[u].a++;
int mid=(l+r)>>1;
if (l==r) return;
if (x<=mid) insert(tree[u].l,tree[v].l,l,mid,x);
else insert(tree[u].r,tree[v].r,mid+1,r,x);
}
int ask(int u,int v,int l,int r,int ql,int qr){
if (ql<=l&&r<=qr) return tree[u].a-tree[v].a;
int mid=(l+r)>>1,ans=0;
if (ql<=mid) ans+=ask(tree[u].l,tree[v].l,l,mid,ql,qr);
if (qr>mid) ans+=ask(tree[u].r,tree[v].r,mid+1,r,ql,qr);
return ans;
}
void pre(){
int tot=n;
for (int i=1;i<=m;i++){
int u=e[i].pre,v=e[i].poi;
if (v==u){s[i]=i; continue;}
if (findroot(u)==findroot(v)){
int p=query(u,v); int t=val[p];
s[i]=t;
cut(e[t].pre,p); cut(e[t].poi,p);
}
tot++;
mn[tot]=tot; val[tot]=i;
link(tot,e[i].pre); link(tot,e[i].poi);
}
for (int i=1;i<=m;i++){
insert(root[i],root[i-1],0,m,s[i]);
}
}
int main(){
n=read(),m=read(),k=read(),tp=read();
val[0]=inf;
for (int i=1;i<=n;i++) mn[i]=i,val[i]=inf;
for (int i=1;i<=m;i++){
e[i].pre=read(); e[i].poi=read();
}
pre();
int ans=0;
for (int i=1;i<=k;i++){
int l=read(),r=read();
if (tp) l^=ans,r^=ans;
ans=n-ask(root[r],root[l-1],0,m,0,l-1);
printf("%d\n",ans);
}
return 0;
}