BZOJ3123:[SDOI2013]森林
阿新 • • 發佈:2018-11-15
淺談主席樹:https://www.cnblogs.com/AKMer/p/9956734.html
題目傳送門:https://www.lydsy.com/JudgeOnline/problem.php?id=3123
如果是一棵樹,維護樹上路徑第\(k\)大,我們令\(rt[i]\)為加入\(i\)號結點之後主席樹的根,若我們在\(rt[fa[i]]\)的基礎上建\(rt[i]\)這棵樹,那麼從每個結點的\(rt[i]\)開始,即可訪問原樹的根到自己這一條路徑上所有權值的\(cnt\),那麼\(cnt[u]+cnt[v]-cnt[lca]-cnt[fa[lca]]\)就是路徑上在該權值區間內的結點個數。由於題目保證\(u,v\)
我們考慮對於合併,如果運用啟發式合併的思想,每次將大小比較小的樹接在大的樹上,然後重構小的樹,每個點最多會被這樣操作\(logn\)次,每次需要更新主席樹上對應的根和倍增陣列,是\(logn\)複雜度的,所以最後就是\(log^2n\)的。因為每個點最多會被建\(logn\)次,每次會建\(logn\)個節點,所以主席樹大小也要開到\(log^2n\)去。
時間複雜度:\(O(nlog^2n)\)
空間複雜度:\(O(nlog^2n)\)
程式碼如下:
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=8e4+5; int tmp[maxn],a[maxn],rt[maxn]; int n,m,q,cnt,tot,lstans,testcase; int now[maxn],pre[maxn*2],son[maxn*2]; int dep[maxn],belong[maxn],siz[maxn],f[maxn][18]; int read() { int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0'; return x*f; } struct tree_node { int cnt,ls,rs; }; struct chairman_tree { int tot; tree_node tree[maxn*17*17]; void updata(int p) { tree[p].cnt=tree[tree[p].ls].cnt+tree[tree[p].rs].cnt; } void ins(int lst,int &now,int l,int r,int pos) { now=++tot;tree[now]=tree[lst]; if(l==r) {tree[now].cnt++;return;} int mid=(l+r)>>1; if(pos<=mid)ins(tree[lst].ls,tree[now].ls,l,mid,pos); else ins(tree[lst].rs,tree[now].rs,mid+1,r,pos); updata(now); } int query(int fa1,int fa2,int u,int v,int l,int r,int rk) { if(l==r)return tmp[l]; int mid=(l+r)>>1; int sum=tree[tree[u].ls].cnt+tree[tree[v].ls].cnt; sum-=(tree[tree[fa1].ls].cnt+tree[tree[fa2].ls].cnt);//sum就是路徑上值在[l,mid]的節點的個數 if(sum>=rk)return query(tree[fa1].ls,tree[fa2].ls,tree[u].ls,tree[v].ls,l,mid,rk); return query(tree[fa1].rs,tree[fa2].rs,tree[u].rs,tree[v].rs,mid+1,r,rk-sum); } }T; void add(int a,int b) { pre[++tot]=now[a]; now[a]=tot;son[tot]=b; } void dfs(int fa,int u,int id) { siz[id]++,belong[u]=id; f[u][0]=fa,dep[u]=dep[fa]+1; for(int i=1;i<=17;i++) f[u][i]=f[f[u][i-1]][i-1]; T.ins(rt[fa],rt[u],1,cnt,a[u]);//每個點建主席樹都在父親主席樹基礎上建 for(int p=now[u],v=son[p];p;p=pre[p],v=son[p]) if(v!=fa)dfs(u,v,id); } int lca(int u,int v) { if(dep[u]<dep[v])swap(u,v); for(int i=17;~i;i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i]; if(u==v)return u; for(int i=17;~i;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; return f[u][0]; } int main() { testcase=read(); n=read();m=read();q=read(); for(int i=1;i<=n;i++) tmp[i]=a[i]=read(); sort(tmp+1,tmp+n+1); cnt=unique(tmp+1,tmp+n+1)-tmp-1; for(int i=1;i<=n;i++) a[i]=lower_bound(tmp+1,tmp+cnt+1,a[i])-tmp; for(int i=1;i<=m;i++) { int x=read(),y=read(); add(x,y);add(y,x); } for(int i=1;i<=n;i++) if(!dep[i])dfs(0,i,i); for(int i=1;i<=q;i++) { char s[5];scanf("%s",s+1); int u=read()^lstans,v=read()^lstans; if(s[1]=='Q') { int k=read()^lstans; int fa=lca(u,v); lstans=T.query(rt[f[fa][0]],rt[fa],rt[u],rt[v],1,cnt,k); printf("%d\n",lstans); } else { int x=belong[u],y=belong[v]; if(siz[x]>siz[y])swap(x,y),swap(u,v); add(u,v),add(v,u),dfs(v,u,y);//把小的往大的上合併 } } return 0; }