BZOJ3545&3551[ONTAK2010]Peaks——kruskal重構樹+主席樹+dfs序+樹上倍增
題目描述
在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之間有雙向道路相連,共M條路徑,每條路徑有一個困難值,這個值越大表示越難走,現在有Q組詢問,每組詢問詢問從點v開始只經過困難值小於等於x的路徑所能到達的山峰中第k高的山峰,如果無解輸出-1。
輸入
第一行三個數N,M,Q。
第二行N個數,第i個數為h_i
接下來M行,每行3個數a b c,表示從a到b有一條困難值為c的雙向路徑。
接下來Q行,每行三個數v x k,表示一組詢問。
輸出
對於每組詢問,輸出一個整數表示答案.(3551強制在線)
樣例輸入
10 11 41 4 4
2 5 3
9 8 2
7 8 10
7 1 4
6 7 1
6 4 8
2 1 5
10 8 10
3 4 7
3 4 6
1 5 2
1 5 6
1 5 8
8 9 2
樣例輸出
61
-1
8
【數據範圍】
N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。
題意要求找出所有與一個點u路徑上最大邊權小於等於x的點中點權第k大。在這裏先介紹一個東西叫做kruskal重構樹,kruskal重構樹就是將kruskal得到的最小生成樹按邊權從小到大進行重構,對於一條邊a——b邊權為c,先建一個新節點x,點權為c,然後把a,b在重構樹中所在子樹的根節點分別連到x的下面,作為x的左右子樹,最後將所有最小生成樹的邊都重構完之後所得到的樹就是kruskal重構樹。例如樣例所建的重構樹如下圖所示,其中黑色點為原最小生成樹的點,紅色點為原最小生成樹的邊在重構樹中對應的點,黑色點內數是原樹點的編號,紅色點內數是原樹對應邊邊權。
kruskal重構樹有幾個性質:
1、這是一棵二叉樹且也是一個大根堆。
2、任意兩個葉子節點在原樹中路徑上最大邊權值為這兩個點在重構樹上lca的點權。
3、原樹兩點間路徑邊權最大值等於重構樹中兩點間路徑點權最大值。
4、任意一個非葉子節點的子樹中所有的葉子節點中,任意兩個節點在原樹中路徑上邊權最大值小於等於這個非葉子節點的點權(本題的關鍵)。
按dfs序把所有重構樹上葉子節點排序,對於每次詢問在重構樹上倍增找到小於等於x的深度最淺的點a,利用主席樹在a點子樹所在dfs序的那段區間上求第k大的點就行了。
最後附上代碼(強制在線)。
#include<map> #include<set> #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define mid (L+R)/2 using namespace std; int cnt; int tot; int num; int top; int ans; int x,y,z; int n,m,k; int a[200010]; int d[200010]; int b[200010]; int q[200010]; int g[200010]; int s[200010]; int t[200010]; int v[200010]; int h[200010]; int to[800010]; int l[6000010]; int r[6000010]; int vis[200010]; int head[200010]; int next[800010]; int sum[6000010]; int root[200010]; int f[200010][20]; struct edge { int x; int y; int z; }e[1000010]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } int find(int x) { if(g[x]==x) { return x; } return g[x]=find(g[x]); } int ST(int x,int y) { for(int i=19;i>=0;i--) { if(d[x]>=b[i]&&v[f[x][i]]<=y) { x=f[x][i]; } } return x; } void add(int x,int y) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; } bool cmp(edge a,edge b) { return a.z<b.z; } void dfs(int x) { vis[x]=1; if(!v[x]) { top++; q[top]=x; } else { s[x]=top; } for(int i=1;i<=19;i++) { if(d[x]>=b[i]) { f[x][i]=f[f[x][i-1]][i-1]; } else { break; } } for(int i=head[x];i;i=next[i]) { d[to[i]]=d[x]+1; f[to[i]][0]=x; dfs(to[i]); } if(x>n) { t[x]=top; } } int build(int L,int R) { int rt=++cnt; sum[rt]=0; if(L<R) { l[rt]=build(L,mid); r[rt]=build(mid+1,R); } return rt; } int updata(int pre,int L,int R,int x) { int rt=++cnt; l[rt]=l[pre]; r[rt]=r[pre]; sum[rt]=sum[pre]+1; if(L<R) { if(x<=mid) { l[rt]=updata(l[pre],L,mid,x); } else { r[rt]=updata(r[pre],mid+1,R,x); } } return rt; } int query(int ll,int rr,int L,int R,int k) { if(L>=R) { return L; } int x=sum[l[rr]]-sum[l[ll]]; if(x>=k) { return query(l[ll],l[rr],L,mid,k); } else { return query(r[ll],r[rr],mid+1,R,k-x); } } int main() { b[0]=1; for(int i=1;i<=19;i++) { b[i]=b[i-1]<<1; } n=read(); m=read(); k=read(); for(int i=1;i<=n;i++) { a[i]=read(); h[i]=a[i]; } sort(h+1,h+1+n); for(int i=1;i<=n;i++) { a[i]=lower_bound(h+1,h+n+1,a[i])-h; } for(int i=1;i<=2*n;i++) { g[i]=i; } for(int i=1;i<=m;i++) { e[i].x=read(); e[i].y=read(); e[i].z=read(); } num=n; sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { int fx=find(e[i].x); int fy=find(e[i].y); if(fx!=fy) { num++; g[fx]=g[fy]=num; v[num]=e[i].z; add(num,fx); add(num,fy); if(num==2*n-1) { break; } } } for(int i=1;i<=n;i++) { if(!vis[i]) { dfs(find(i)); } } root[0]=build(1,n); for(int i=1;i<=top;i++) { root[i]=updata(root[i-1],1,n,a[q[i]]); } while(k--) { x=read(); y=read(); z=read(); if(ans!=-1) { x^=ans; y^=ans; z^=ans; } int j=ST(x,y); int adc=root[s[j]]; int apc=root[t[j]]; if(sum[apc]-sum[adc]<z) { ans=-1; } else { ans=h[query(adc,apc,1,n,sum[apc]-sum[adc]-z+1)]; } printf("%d\n",ans); } }
BZOJ3545&3551[ONTAK2010]Peaks——kruskal重構樹+主席樹+dfs序+樹上倍增