BZOJ4317Atm的樹&BZOJ2051A Problem For Fun&BZOJ2117[2010國家集訓隊]Crash的旅遊計劃——二分答案+動態點分治(點分樹套線段樹/點分樹+vector)
阿新 • • 發佈:2018-12-19
題目描述
Atm有一段時間在虐qtree的題目,於是,他滿腦子都是tree,tree,tree…… 於是,一天晚上他夢到自己被關在了一個有根樹中,每條路徑都有邊權,一個神祕的聲音告訴他,每個點到其他的點有一個距離(什麼是距離不用說吧),他需要對於每個點回答:從這個點出發的第k小距離是多少; 如果atm不能回答出來,那麼明天4019的鬧鐘將不會響,4019全寢可能就遲到了,所以atm希望你幫幫他。輸入
第一行,兩個正整數n,k,表示樹的點數,詢問的是第幾小距離; 第二~n行,每行三個正整數x,y,w,表示x和y之間有一條邊,x為父親,邊權為w;輸出
樣例輸入
5 21 5 2
1 2 4
2 3 6
2 4 5
樣例輸出
45
10
9
6
提示
100% n<=15000, 邊權在1~10之間,為了方便,保證1為根;K<=5000 我們先考慮對於單次詢問如何用點分治來解決。 對於每個分治中心,如果它處理的聯通塊中包含查詢點,那麼需要記錄下來聯通塊中所有點到分治中心的距離+分治中心到查詢點的距離,然後容斥去掉與查詢點位於分治中心同一棵子樹中的點,遞迴下一層,最後在所有記錄的距離中取第k小的。#include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; int root[15010]; int froot[15010]; int ls[6000010]; int rs[6000010]; int sum[6000010]; int n,k; int x,y,z; int tot; int num; int dfn; int f[15010]; int g[30010][16]; int lg[30010]; int dep[15010]; int val[30010]; int to[30010]; int next[30010]; int head[15010]; int size[15010]; int s[15010]; int rot; int cnt; int mx[15010]; int ans; int vis[15010]; int l,r; inline void add(int x,int y,int z) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; val[tot]=z; } inline void dfs(int x,int fa) { g[++dfn][0]=dep[x]; s[x]=dfn; for(int i=head[x];i;i=next[i]) { if(to[i]!=fa) { dep[to[i]]=dep[x]+val[i]; dfs(to[i],x); g[++dfn][0]=dep[x]; } } } inline void getroot(int x,int fa) { size[x]=1; mx[x]=0; for(int i=head[x];i;i=next[i]) { if(!vis[to[i]]&&to[i]!=fa) { getroot(to[i],x); size[x]+=size[to[i]]; mx[x]=max(mx[x],size[to[i]]); } } mx[x]=max(mx[x],num-size[x]); if(mx[x]<mx[rot]) { rot=x; } } inline int lca(int x,int y) { x=s[x]; y=s[y]; if(x>y) { swap(x,y); } int len=lg[y-x+1]; return min(g[x][len],g[y-(1<<len)+1][len]); } inline int dis(int x,int y) { return dep[x]+dep[y]-2*lca(x,y); } inline void partation(int x) { vis[x]=1; for(int i=head[x];i;i=next[i]) { if(!vis[to[i]]) { num=size[to[i]]; rot=0; getroot(to[i],0); f[rot]=x; partation(rot); } } } inline void change(int &rt,int l,int r,int k) { if(!rt) { rt=++cnt; } sum[rt]++; if(l==r) { return ; } int mid=(l+r)>>1; if(k<=mid) { change(ls[rt],l,mid,k); } else { change(rs[rt],mid+1,r,k); } } inline int query(int rt,int l,int r,int k) { if(!rt||k<0) { return 0; } if(l==r) { return sum[rt]; } int mid=(l+r)>>1; if(k<=mid) { return query(ls[rt],l,mid,k); } else { return sum[ls[rt]]+query(rs[rt],mid+1,r,k); } } inline int check(int val,int x) { int res=0; for(int i=x;i;i=f[i]) { res+=query(root[i],0,150000,val-dis(x,i)); } for(int i=x;f[i];i=f[i]) { res-=query(froot[i],0,150000,val-dis(x,f[i])); } return res; } int main() { scanf("%d%d",&n,&k); k++; for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } dfs(1,0); for(int i=2;i<=dfn;i++) { lg[i]=lg[i>>1]+1; } for(int j=1;j<=15;j++) { for(int i=1;i<=dfn;i++) { if(i+(1<<j)-1>dfn) { break; } g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]); } } mx[0]=1<<30; num=n; rot=0; getroot(1,0); partation(rot); for(int x=1;x<=n;x++) { for(int i=x;i;i=f[i]) { change(root[i],0,150000,dis(x,i)); } for(int i=x;f[i];i=f[i]) { change(froot[i],0,150000,dis(x,f[i])); } } for(int i=1;i<=n;i++) { l=0; r=150000; ans=-1; while(l<=r) { int mid=(l+r)>>1; if(check(mid,i)>=k) { ans=mid; r=mid-1; } else { l=mid+1; } } printf("%d\n",ans); } }
點分樹+vector
#include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; vector<int>sum[100010]; vector<int>fsum[100010]; int n,k; int x,y,z; int tot; int num; int dfn; char ch[3]; int f[100010]; int g[200010][17]; int lg[200010]; int dep[100010]; int val[200010]; int to[200010]; int next[200010]; int head[100010]; int size[100010]; int s[100010]; int rot; int cnt; int mx[100010]; int ans; int vis[100010]; int l,r; int length; void add(int x,int y,int z) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; val[tot]=z; } void dfs(int x,int fa) { g[++dfn][0]=dep[x]; s[x]=dfn; for(int i=head[x];i;i=next[i]) { if(to[i]!=fa) { dep[to[i]]=dep[x]+val[i]; dfs(to[i],x); g[++dfn][0]=dep[x]; } } } void getroot(int x,int fa) { size[x]=1; mx[x]=0; for(int i=head[x];i;i=next[i]) { if(!vis[to[i]]&&to[i]!=fa) { getroot(to[i],x); size[x]+=size[to[i]]; mx[x]=max(mx[x],size[to[i]]); } } mx[x]=max(mx[x],num-size[x]); if(mx[x]<mx[rot]) { rot=x; } } int lca(int x,int y) { x=s[x]; y=s[y]; if(x>y) { swap(x,y); } int len=lg[y-x+1]; return min(g[x][len],g[y-(1<<len)+1][len]); } int dis(int x,int y) { return dep[x]+dep[y]-(lca(x,y)<<1); } void partation(int x) { vis[x]=1; for(int i=head[x];i;i=next[i]) { if(!vis[to[i]]) { num=size[to[i]]; rot=0; getroot(to[i],0); f[rot]=x; partation(rot); } } } int check(int val,int x) { int res=0; for(int i=x;i;i=f[i]) { res+=upper_bound(sum[i].begin(),sum[i].end(),val-dis(x,i))-sum[i].begin(); } for(int i=x;f[i];i=f[i]) { res-=upper_bound(fsum[i].begin(),fsum[i].end(),val-dis(x,f[i]))-fsum[i].begin(); } return res; } int main() { scanf("%s",ch); scanf("%d%d",&n,&k); k++; for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); length+=z; add(x,y,z); add(y,x,z); } dfs(1,0); for(int i=2;i<=dfn;i++) { lg[i]=lg[i>>1]+1; } for(int j=1;j<=16;j++) { for(int i=1;i<=dfn;i++) { if(i+(1<<j)-1>dfn) { break; } g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]); } } mx[0]=1<<30; num=n; rot=0; getroot(1,0); partation(rot); for(int x=1;x<=n;x++) { for(int i=x;i;i=f[i]) { sum[i].push_back(dis(i,x)); } for(int i=x;f[i];i=f[i]) { fsum[i].push_back(dis(f[i],x)); } } for(int i=1;i<=n;i++) { sort(sum[i].begin(),sum[i].end()); sort(fsum[i].begin(),fsum[i].end()); } for(int i=1;i<=n;i++) { l=0; r=length; ans=-1; while(l<=r) { int mid=(l+r)>>1; if(check(mid,i)>=k) { ans=mid; r=mid-1; } else { l=mid+1; } } printf("%d\n",ans); } }