题目描述
小强要在个孤立的星球上建立起一套通信系统。这套通信系统就是连接个点的一个树。 这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它的简单路径的数量。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。

离线
在查询的时候,如果我们把这条边删掉,那么答案就很好求
观察到有删、加边,用线段树分治即可
具体而言,对于每一个查询,时间为,和上一次查询(若没有则是添加它的时间),在加入这条边
最后一次查询加入到
可撤销并查集即可

#include <bits/stdc++.h>
#define ri register int
#define ll long long
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
const int Maxn=1e5;
struct Query {
    int x,y,id;
};
vector<int>r[Maxn+5];
map<pii,int>id;
pii e[Maxn+5];
int l[Maxn+5],n,q;
struct UnionSet {
    int s[Maxn+5],top;
    int father[Maxn+5],size[Maxn+5];
    void init() {
        for(ri i=1;i<=n;i++)father[i]=i,size[i]=1;
    }
    int getfather(int v) {
        return father[v]==v?v:getfather(father[v]);
    }
    void merge(int x,int y) {
        int fx=getfather(x),fy=getfather(y);
        if(size[fx]<size[fy])swap(fx,fy);
        s[++top]=fy;
        father[fy]=fx,size[fx]+=size[fy];
    }
    void del(int k) {
        while(top>k) {
            int t=s[top--];
            size[father[t]]-=size[t];
            father[t]=t;
        }
    }
}us;
struct SegTree {
    #define ls(p) (p<<1)
    #define rs(p) (p<<1|1)
    #define mid (((l)+(r))>>1)
    vector<pii>v[Maxn<<2];
    Query q[Maxn+5];
    void change(int p,int l,int r,int L,int R,pii d) {
        if(L>R)return ;
        if(L<=l&&r<=R) {
            v[p].push_back(d);return ;
        }
        if(L<=mid)change(ls(p),l,mid,L,R,d);
        if(R>mid)change(rs(p),mid+1,r,L,R,d);
    }
    void solve(int p,int l,int r) {
        int size=(int)v[p].size(),u=us.top;
        for(ri i=0;i<size;i++)us.merge(v[p][i].x,v[p][i].y);
        if(l==r) {
            if(q[l].id) {
                ll ans=1ll*us.size[us.getfather(q[l].x)]*us.size[us.getfather(q[l].y)];
                printf("%lld\n",ans);    
            }
        }
        else {
            solve(ls(p),l,mid);
            solve(rs(p),mid+1,r);
        }
        us.del(u);
    }
}t;
int main() {
    scanf("%d%d",&n,&q);
    int cnt=0,tot=0;
    for(ri i=1;i<=q;i++) {
        char c=getchar();
        while(c!='A'&&c!='Q')c=getchar();
        pii s;
        scanf("%d%d",&s.x,&s.y);
        if(c=='A') {
            e[++cnt]=s;id[s]=cnt;l[cnt]=i;
        }
        else {
            r[id[s]].push_back(i);t.q[i]=(Query){s.x,s.y,++tot};
        }
    }
    for(ri i=1;i<=cnt;i++) {
        int size=(int)r[i].size(),now=l[i];
        for(ri j=0;j<size;j++) {
            t.change(1,1,q,now,r[i][j]-1,e[i]);
            now=r[i][j]+1;
        }
        t.change(1,1,q,now,q,e[i]);
    }
    us.init();
    t.solve(1,1,q);
    return 0;
}

在线
还是考虑删边
我们有两种思路

处理只不添加第条边的图,每次查询 回退到第条边,再回加
这两种方法复杂度都很高
那结合呢?
分块
个操作记录在一块内
个操作建一个新图,表示不加如这条边,而加入其余所有已加边的图
每次查询找到这个操作对应的块
在对应块的图上将除这条边外所有这块内的边加入
这就可以求了
可撤销并查集回退一下
注意每一次加边要将之前所有的块的图加入这条边,新建一个块要将除这块外之前所有的边加入
时间复杂度,空间复杂度

#include <bits/stdc++.h>
#define ri register int
#define ll long long
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
const int Maxn=1e5;
const int Maxm=320;
map<pii,int>id;
vector<pii>v[Maxm+5];
int n,q,T;
int bel[Maxn+5],Block;
static char buf[30000000],*p1=buf,*p2=buf,obuf[30000000],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
#define putchar(x) (p3-obuf<1000000)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>
inline void read(register item &x) {
    x=0;register char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
static char cc[7000000];
template<typename item>
inline void print(register item x) {
    ri len=0;
    do {cc[len++]=x%10+'0',x/=10;}while(x);
    while(len--)putchar(cc[len]);
}
struct UnionSet {
    int s[Maxn+5],top;
    int father[Maxn+5],size[Maxn+5];
    inline void init() {
        for(ri i=1;i<=n;i++)father[i]=i,size[i]=1;
    }
    inline int getfather(int v) {
        return father[v]==v?v:getfather(father[v]);
    }
    inline void merge(int x,int y) {
        int fx=getfather(x),fy=getfather(y);
        if(fx!=fy) {
            if(size[fx]<size[fy])swap(fx,fy);
            s[++top]=fy;
            father[fy]=fx,size[fx]+=size[fy];
        }
    }
    inline void del(int k) {
        while(top>k) {
            int t=s[top--];
            size[father[t]]-=size[t];
            father[t]=t;
        }
    }
}us[Maxm+5];
inline ll query(int x,int y) {
    int block=bel[id[make_pair(x,y)]];
    int u=us[block].top;
    for(ri i=0;i<(int)v[block].size();i++) {
        pii t=v[block][i];
        if(t.x!=x||t.y!=y)us[block].merge(t.x,t.y);
    }
    ll ans=us[block].size[us[block].getfather(x)]*us[block].size[us[block].getfather(y)];
    us[block].del(u);
    return ans;
}
inline void build() {
    ++Block;
    us[Block].init();
    for(ri i=1;i<Block;i++) {
        for(ri j=0;j<(int)v[i].size();j++) {
            pii t=v[i][j];
            us[Block].merge(t.x,t.y);
        }
    }
}
int main() {
    read(n),read(q),read(T);
    ll ans=0;
    int cnt=0,Size=320;
    build();
    for(ri i=1;i<=q;i++) {
        char c=getchar();
        while(c!='A'&&c!='Q')c=getchar();
        int x,y;
        read(x),read(y);
        if(T)x^=ans,y^=ans;
        if(c=='A') {
            pii t;t.x=x,t.y=y;
            for(ri j=1;j<Block;j++)us[j].merge(t.x,t.y);
            v[Block].push_back(t);
            id[t]=++cnt;bel[cnt]=(cnt-1)/Size+1;
            if(cnt%Size==0)build();
        }
        else print(ans=query(x,y)),putchar('\n');
    }
    fwrite(obuf,p3-obuf,1,stdout);
    return 0;
}