一年前的寒假我应该是做过这题的不强制在线的版本(就是原题),当时的做法是离线+LCT+树状数组
不过强制在线之后也大同小异,都是从时间戳和生成树的角度来考虑的
考虑如果我们运气好整张图就是一个森林那么答案怎么算,显然就是\(边数n-\text{边数}\)
那么我们换个角度考虑,现在每条边对答案的贡献是\(-1\)
但是如果运气不好,加入一条边的时候成环了怎么办,显然此时联通块个数不会变化,那么这条边对答案的贡献就是\(0\)
那么我们容易想到如果能求出区间内的每条边的贡献是多少就可以算答案了
对于最早加入的边(即没有与别人成环),它们显然只要在询问区间内就有贡献,但是后面加入与它们成环的边什么时候才会有贡献呢?
顺着这个思路我们容易想到在加入一条成环的边的时候,记这个环上最早加入的边编号为\(p\),那么如果询问的\(l>p\)那么这条边就有贡献了,因为与它成环的边没有被加入
那么我们的思路就很清晰了,对于每次加入的边\(i\)我们求出与它成环的边的最早的编号记为\(lst_i\)(若没有成环则为\(0\))
然后对于一个询问的区间\([l,r]\),找出区间内有多少个位置\(lst_i
显然用主席树可以轻松地维护上述问题,总复杂度\(O(n\log n)\)
#include
#include
#include
#define RI register int
#define CI const int&
#define Tp template
using namespace std;
const int N=200005,INF=1e9;
struct edge
{
int x,y;
}e[N]; int n,m,l,r,q,tp,lstans,lst[N],val[N<<1];
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[15];
public:
inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
RI ptop=0; while (pt[++ptop]=x%10,x/=10);
while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void flush(void)
{
fwrite(Fout,1,Ftop-Fout,stdout);
}
#undef tc
#undef pc
}F;
class Link_Cut_Tree
{
private:
struct splay
{
int ch[2],fa,mi; bool rev;
}node[N<<1]; int stk[N<<1],top;
#define lc(x) node[x].ch[0]
#define rc(x) node[x].ch[1]
#define fa(x) node[x].fa
#define Mi(x) node[x].mi
#define Rv(x) node[x].rev
inline void rever(CI now)
{
swap(lc(now),rc(now)); Rv(now)^=1;
}
inline void pushup(CI now)
{
Mi(now)=now; if (val[Mi(lc(now))]>1;
if (pos<=mid) _modify(lc(lst),lc(now),pos,l,mid); else _modify(rc(lst),rc(now),pos,mid+1,r);
}
inline int _query(CI x,CI y,CI beg,CI end,TN)
{
if (beg<=l&&r<=end) return S(y)-S(x); int mid=l+r>>1,ret=0;
if (beg<=mid) ret+=_query(lc(x),lc(y),beg,end,l,mid);
if (end>mid) ret+=_query(rc(x),rc(y),beg,end,mid+1,r); return ret;
}
public:
inline void modify(CI pos,CI val)
{
_modify(rt[pos-1],rt[pos],val);
}
inline int query(CI l,CI r)
{
return _query(rt[l-1],rt[r],0,l-1);
}
#undef lc
#undef rc
#undef S
#undef TN
}SEG;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),F.read(m),F.read(q),F.read(tp),i=1;i<=n;++i) LCT.insert(i,INF);
for (val[0]=INF,i=1;i<=m;++i)
{
F.read(e[i].x); F.read(e[i].y); LCT.insert(n+i,i);
if (e[i].x==e[i].y) { lst[i]=m; continue; }
if (LCT.findroot(e[i].x)==LCT.findroot(e[i].y))
{
int pos=LCT.query(e[i].x,e[i].y); lst[i]=pos-n;
LCT.cut(pos,e[pos-n].x); LCT.cut(pos,e[pos-n].y);
}
LCT.link(n+i,e[i].x); LCT.link(n+i,e[i].y);
}
for (i=1;i<=m;++i) SEG.modify(i,lst[i]); for (i=1;i<=q;++i)
{
F.read(l); F.read(r); if (tp) l^=lstans,r^=lstans;
F.write(lstans=n-SEG.query(l,r));
}
return F.flush(),0;
}