bzoj3514 Codechef MARCH14 GERALD07加强版
有些纪念意义的题
$N$个点$M$条边的无向图,$Q$次询问保留图中编号在$[l,r]$的边的时候图中的联通块个数,强制在线。
$N,M,Q \leq 200000$
受某远古$CF$题的影响,大力$LCT$硬搞
一个神奇的做法:
令每条边边权为加入时间
搞一个数组$used$,加边的时候如果形成了环,则弹掉边权最小的那条边,并记录$used[i]=j$表示$i$弹掉了$j$
特别地,当i上有一个自环时$used[i]=i$,不弹掉的话$used[i]=0$
这样的话每个询问的答案就是$n-[l,r]$区间上$used$值小于$l$的边的数量
正确性:
如果一条边的$used$值不小于$l$,说明它能跟$[l,r]$上的边连成一个环,对答案没有贡献
如果小于$l$,说明是一条新边,对答案的贡献为$-1$
于是开个树套树/主席树记一下$[l,r]$区间上小于$k$的数的个数就好啦
/************************************************************** Problem: 3514 User: Ez3real Language: C++ Result: Accepted Time:30504 ms Memory:79436 kb ****************************************************************/ #include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x=0,f=1;char ch; for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')f=-f; for(;isdigit(ch);ch=getchar())x=10*x+ch-'0'; return x*f; } const int maxn = 400010 , inf = 1e9; int n,m,q,type,lastans; namespace LCT { int son[maxn][2],fa[maxn],rev[maxn]; int mn[maxn],val[maxn]; int st[maxn],top; #define lc son[x][0] #define rc son[x][1] inline int isroot(int x){return (son[fa[x]][0] != x) && (son[fa[x]][1] != x);} inline void pushup(int x) { mn[x] = x; if(val[mn[lc]] < val[mn[x]]) mn[x] = mn[lc]; if(val[mn[rc]] < val[mn[x]]) mn[x] = mn[rc]; } inline void pushdown(int x) { if(rev[x]) { rev[x] ^= 1; rev[lc] ^= 1; rev[rc] ^= 1; swap(lc,rc); } } inline void rotate(int x) { int y=fa[x],z=fa[y]; int l=(son[y][1] == x),r = l ^ 1; if(!isroot(y))son[z][son[z][1] == y] = x; fa[x] = z,fa[y] = x, fa[son[x][r]] = y; son[y][l] = son[x][r] , son[x][r] = y ; pushup(y),pushup(x); } inline void splay(int x) { st[top=1]=x; for(int i=x;!isroot(i);i=fa[i])st[++top]=fa[i]; for(int i=top;i;i--)pushdown(st[i]); while(!isroot(x)) { int y=fa[x],z=fa[y]; if(!isroot(y)) { if(son[y][0] == x ^ son[z][0] == y)rotate(x); else rotate(y); } rotate(x); } } inline void access(int x) { for(int y=0;x;y=x,x=fa[x])splay(x),son[x][1] = y,pushup(x); } inline void makert(int x) { access(x),splay(x),rev[x]^=1; } inline void link(int x,int y) { makert(x),fa[x]=y; } inline void split(int x,int y) { makert(x),access(y),splay(y); } inline void cut(int x,int y) { split(x,y); son[y][0]=fa[x]=0; } inline int find(int x) { access(x),splay(x); while(son[x][0]) x = son[x][0]; return x; } inline int LCquery(int x,int y) { split(x,y); return mn[y]; } } namespace HujinTree { int SIZE; const int MAXNODE = 4000010; int root[MAXNODE]; int ls[MAXNODE],rs[MAXNODE],nval[MAXNODE]; inline void insert(int &cur,int l,int r,int pre,int va) { cur = ++SIZE; nval[cur] = nval[pre] + 1; if(l == r)return; ls[cur] = ls[pre] , rs[cur] = rs[pre]; int mid = (l+r) >> 1; if(va <= mid) insert(ls[cur],l,mid,ls[pre],va); else insert(rs[cur],mid+1,r,rs[pre],va); } inline int query(int cur,int l,int r,int pre,int va) { if(r == va)return nval[cur] - nval[pre]; int mid = (l+r) >> 1; if(va <= mid)return query(ls[cur],l,mid,ls[pre],va); else return nval[ls[cur]] - nval[ls[pre]] + query(rs[cur],mid+1,r,rs[pre],va); } } namespace LCGraph { using namespace HujinTree; using namespace LCT; struct EDG{int u,v;}es[maxn]; int used[maxn]; inline int main() { n=read(),m=read(),q=read(),type=read(); val[0] = inf; for(int i=1;i<=n;i++)mn[i] = i, val[i] = inf; for(int i=1;i<=m;i++)es[i].u=read(),es[i].v=read(); //return 0; int temp = n; for(int i=1;i<=m;i++) { int uu = es[i].u, vv = es[i].v; if(uu == vv){used[i] = i;continue;} if(find(uu) == find(vv)) { int a = LCquery(uu,vv); int av = val[a]; used[i] = av; //return 0; cut(uu,a),cut(vv,a); } mn[++temp] = temp;val[temp] = i; link(uu,temp) ,link(vv,temp); } for(int i=1;i<=m;i++) insert(root[i],0,m,root[i-1],used[i]); //return 0; while(q--) { int l,r; l=read(),r=read(); if(type == 1)l ^= lastans, r ^= lastans; lastans = n - query(root[r],0,m,root[l-1],l-1); //lastans = query(root[r],0,m,root[l-1],l-1); printf("%d\n",lastans); } } } int main() { LCGraph::main(); return 0; }
人傻常数大,一开始60s过不了第一个点QAQ