bzoj 4530(DFS序+线段树合并)

来源:互联网 发布:mac 双定制粉底液 肤质 编辑:程序博客网 时间:2024/06/02 02:04

传送门
题解:对每个点建权值线段树(权值即点在DFS序列中的编号),合并的时候直接合并两个点根的线段树,并连一下并查集,查询的时候找到x,y所在树的根f,假设dep[x]>dep[y],那答案就是(size[f]-size[x])*size[x](这一点的解释尽快补上,不过好像不比较也能过,难道是数据水?)。
P.S.在询问时要选深度更深的点的原因:
如果dep[x] < dep[y]并且选择了x,那么x的子树就包括了y点,而答案是两边子树大小的乘积,所以这样会造成多余贡献,不合法。
这里写图片描述

#include<bits/stdc++.h>using namespace std;const int MAXN=1e5+4;int n,m;int head[MAXN],fa[MAXN],edge=0;int dep[MAXN],in[MAXN],out[MAXN],tim=0;bool vis[MAXN];struct EDGE {    int v,nxt;}e[MAXN<<1];struct Q {    int opt,x,y;}q[MAXN];int siz[MAXN*18],lc[MAXN*18],rc[MAXN*18],root[MAXN],tot=0;int find(int x) {    return fa[x]==x?x:fa[x]=find(fa[x]);}inline void adde(int u,int v) {    e[++edge].nxt=head[u],e[edge].v=v,head[u]=edge;    e[++edge].nxt=head[v],e[edge].v=u,head[v]=edge;}inline int read() {    int x=0;char c=getchar();    while (c<'0'||c>'9') c=getchar();    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();    return x;}inline void pushup(int rt) {    siz[rt]=siz[lc[rt]]+siz[rc[rt]];}void insert(int &rt,int l,int r,int val) {    rt=++tot;    if (l==r) {siz[rt]=1;return ;}    int mid=(l+r)>>1;    if (val<=mid) insert(lc[rt],l,mid,val);    else insert(rc[rt],mid+1,r,val);    pushup(rt);}inline int merge(int a,int b) {    if (!a) return b;    if (!b) return a;    lc[a]=merge(lc[a],lc[b]);    rc[a]=merge(rc[a],rc[b]);    pushup(a);    return a;}int query(int rt,int l,int r,int L,int R) {    if (L<=l&&r<=R) return siz[rt];    int mid=(l+r)>>1,ret=0;    if (L<=mid) ret+=query(lc[rt],l,mid,L,R);    if (mid<R) ret+=query(rc[rt],mid+1,r,L,R);    return ret;}void dfs(int p,int fa) {    in[p]=++tim,vis[p]=true;    insert(root[p],1,n,in[p]);    for (int i=head[p];~i;i=e[i].nxt) {        int v=e[i].v;        if (v^fa)            dep[v]=dep[p]+1,dfs(v,p);    }    out[p]=tim;}int main() {//  freopen("bzoj 4530.in","r",stdin);    memset(head,-1,sizeof(head));    memset(vis,false,sizeof(vis));    n=read(),m=read();    for (register int i=1;i<=m;++i) {        char ss;        while (!isalpha(ss=getchar()));        q[i].opt=(ss^'Q')?1:2;        q[i].x=read(),q[i].y=read();        if (q[i].opt&1) adde(q[i].x,q[i].y);    }    for (register int i=1;i<=n;++i){        fa[i]=i;        if (!vis[i]) dep[i]=0,dfs(i,0);    }/*  for (int i=1;i<=n;++i)        cout<<dep[i]<<' ';    cout<<endl;*/    for (register int i=1;i<=m;++i) {        if (q[i].opt&1) {            int x=q[i].x,y=q[i].y;            x=find(x),y=find(y);            if (dep[x]>dep[y]) x^=y^=x^=y;//find the earlier-linked node(new node link to old node)            root[x]=merge(root[x],root[y]);            fa[y]=x;        }        else {            int x=q[i].x,y=q[i].y;            int pos=(dep[x]<dep[y])?y:x;//find the later-linked node to avoid repeated calculation            int f=find(pos);            int temp=query(root[f],1,n,in[pos],out[pos]);            printf("%lld\n",1ll*(siz[root[f]]-temp)*temp);        }    }    return 0;}