1. 程式人生 > >BZOJ3123: [Sdoi2013]森林(啟發式合併&主席樹)

BZOJ3123: [Sdoi2013]森林(啟發式合併&主席樹)

3123: [Sdoi2013]森林

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 4813  Solved: 1420
[Submit][Status][Discuss]

Description

Input

第一行包含一個正整數testcase,表示當前測試資料的測試點編號。保證1≤testcase≤20。 
第二行包含三個整數N,M,T,分別表示節點數、初始邊數、運算元。第三行包含N個非負整數表示 N個節點上的權值。 
 接下來 M行,每行包含兩個整數x和 y,表示初始的時候,點x和點y 之間有一條無向邊, 接下來 T行,每行描述一個操作,格式為“Q x y k”或者“L x y ”,其含義見題目描述部分。

Output

對於每一個第一類操作,輸出一個非負整數表示答案。 
 
 

Sample Input

1
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6

Sample Output

2
2
1
4
2

HINT



對於第一個操作 Q 8 7 3,此時 lastans=0,所以真實操作為Q 8^0 7^0 3^0,也即Q 8 7 3。點8到點7的路徑上一共有5個點,其權值為4 1 1 2 4。這些權值中,第三小的為 2,輸出 2,lastans變為2。對於第二個操作 Q 3 5 1 ,此時lastans=2,所以真實操作為Q 3^2 5^2 1^2 ,也即Q 1 7 3。點1到點7的路徑上一共有4個點,其權值為 1 1 2 4 。這些權值中,第三小的為2,輸出2,lastans變為 2。之後的操作類似。 


 

思路:題意輸入的case不要管,此題不是多組輸入。 我們求路徑第k大,優先會想到主席樹,但是這裡有合併的操作,事實上啟發式夠用了。

至於倍增LCA,我們可以dfs的時候就維護。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=80010;
struct in{
    int l,r,sum;
    in(){l=r=sum=0;}
    in(int L,int R,int S):l(L),r(R),sum(S){}
}s[
10000010]; int Laxt[maxn],Next[maxn<<1],To[maxn<<1],rt[maxn],cnt,scc_cnt,N,ans; int a[maxn],b[maxn],fa[maxn][17],tot,scc[maxn],sz[maxn],dep[maxn],num; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } void add(int u,int v){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;} void update(int &Now,int pre,int L,int R,int pos) { Now=++num; s[Now]=s[pre]; s[Now].sum++; if(L==R) return ; int Mid=(L+R)>>1; if(pos<=Mid) update(s[Now].l,s[pre].l,L,Mid,pos); else update(s[Now].r,s[pre].r,Mid+1,R,pos); } void dfs(int u,int f,int p) { update(rt[u],rt[f],1,tot,a[u]); dep[u]=dep[f]+1; fa[u][0]=f; scc[u]=p; sz[p]++; rep(j,1,16) fa[u][j]=fa[fa[u][j-1]][j-1]; for(int i=Laxt[u];i;i=Next[i]) if(To[i]!=f) dfs(To[i],u,p); } void Connect(int x,int y) { if(sz[scc[x]]<sz[scc[y]]) swap(x,y); dfs(y,x,scc[x]); } int query(int u,int v,int Lca,int old,int L,int R,int k) { if(L==R) return L; int Mid=(L+R)>>1; int tmp=s[s[u].l].sum+s[s[v].l].sum-s[s[Lca].l].sum-s[s[old].l].sum; if(tmp>=k) return query(s[u].l,s[v].l,s[Lca].l,s[old].l,L,Mid,k); return query(s[u].r,s[v].r,s[Lca].r,s[old].r,Mid+1,R,k-tmp); } int LCA(int u,int v) { if(dep[u]<dep[v]) swap(u,v); for(int i=16;i>=0;i--) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i]; if(u==v) return u; for(int i=16;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } void Query(int u,int v,int k) { int Lca=LCA(u,v); ans=b[query(rt[u],rt[v],rt[Lca],rt[fa[Lca][0]],1,tot,k)]; printf("%d\n",ans); } int main() { int C,M,T,u,v,x,y,k; scanf("%d%d%d%d",&C,&N,&M,&T); rep(i,1,N) read(a[i]),b[i]=a[i]; sort(b+1,b+N+1); tot=unique(b+1,b+N+1)-(b+1); rep(i,1,N) a[i]=lower_bound(b+1,b+tot+1,a[i])-b; rep(i,1,M){ read(u); read(v); add(u,v); add(v,u); } char opt[3]; rep(i,1,N) if(!scc[i]) dfs(i,0,++scc_cnt); while(T--){ scanf("%s",opt); if(opt[0]=='Q'){ read(x); read(y); read(k); x=ans^x; y=ans^y; k=ans^k; Query(x,y,k); } else { scanf("%d%d",&x,&y); x=ans^x; y=ans^y; add(x,y); add(y,x); Connect(x,y); } } return 0; }