1. 程式人生 > >[SDOI2011]消防(單調隊列,樹的直徑,雙指針)

[SDOI2011]消防(單調隊列,樹的直徑,雙指針)

最大 r+ ostream -1 logs mat -o span 國家

消防

2011年

時間限制: 2 s 空間限制: 256000 KB 題目等級 : 大師 Master
題目描述 Description

某個國家有n個城市,這n個城市中任意兩個都連通且有唯一一條路徑,每條連通兩個城市的道路的長度為zi(zi<=1000)。

這個國家的人對火焰有超越宇宙的熱情,所以這個國家最興旺的行業是消防業。由於政府對國民的熱情忍無可忍(大量的消防經費開銷)可是卻又無可奈何(總統競選的國民支持率),所以只能想盡方法提高消防能力。

現在這個國家的經費足以在一條邊長度和不超過s的路徑(兩端都是城市)上建立消防樞紐,為了盡量提高樞紐的利用率,要求其他所有城市到這條路徑的距離的最大值最小。

你受命監管這個項目,你當然需要知道應該把樞紐建立在什麽位置上。

輸入描述 Input Description

輸入包含n行:
第1行,兩個正整數n和s,中間用一個空格隔開。其中n為城市的個數,s為路徑長度的上界。設結點編號以此為1,2,……,n。
從第2行到第n行,每行給出3個用空格隔開的正整數,依次表示每一條邊的兩個端點編號和長度。例如,“2 4 7”表示連接結點2與4的邊的長度為7。

輸出描述 Output Description

輸出包含一個非負整數,即所有城市到選擇的路徑的最大值,當然這個最大值必須是所有方案中最小的。

樣例輸入 Sample Input

【樣例輸入1】

5 2
1 2 5
2 3 2
2 4 4
2 5 3

【樣例輸入2】

8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3

樣例輸出 Sample Output

【樣例輸出1】

5

【樣例輸出2】

5

數據範圍及提示 Data Size & Hint

對於20%的數據,n<=300。

對於50%的數據,n<=3000。

對於100%的數據,n<=300000,邊長小等於1000。

/*
20%的數據,就是NOIP那道樹網的核。
100%這麽大的數據範圍,若直徑長度為d,dlogd可能會被卡
所以就需要一種O(n)的算法。

先bfs兩遍求出樹的直徑(防爆棧),O(n)的
然後維護 g[i]表示i是路徑右端點時,右邊那段刪掉的直徑長度。
f[i]表示i是路徑左端點時,左邊那段刪掉的直徑長度。
h[i]表示i是直徑上的點,每個直徑上的點不是都有一棵(或者很多棵)
由非此直徑上點組成的樹(森林)嘛,點i到這些子節點中最遠的那個的距離。
然後在這個序列上跑雙指針。就是路徑長度不是有限制嘛,然後從左到右枚舉左端點,然後右端點是非嚴格單調右移的。
時間復雜度線性。而對於一段路徑區間[l,r],它作為樞紐時的答案為 
max(max(f[l],g[r]),max{hi,i∈[l,r]}) 
然後最右邊那個怎麽搞呢? 單調隊列,維護hi最大值啊~ 
*/ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define N 3000007 #define inf 0x3f3f3f3f using namespace std; int n,m,k,s,t,ans,cnt,num,L,R; int head[N],f[N],g[N],h[N],fa[N],vis[N]; int belong[N],tmp[N],que[N],id[N]; struct edge{ int v,net,w; }e[N<<1]; queue<int>q; inline int read() { int x=0,f=1;char c=getchar(); while(c>9||c<0){if(c==-)f=-1;c=getchar();} while(c>=0&&c<=9){x=x*10+c-0;c=getchar();} return x*f; } inline void add(int u,int v,int w) { e[++cnt].v=v;e[cnt].w=w,e[cnt].net=head[u];head[u]=cnt; } int bfs(int s,int Time) { int len=0;q.push(s); g[s]=0,fa[s]=s,vis[s]=Time; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].net) { int v=e[i].v; if(vis[v]==Time) continue; g[v]=g[u]+e[i].w;len=max(len,g[v]); fa[v]=u;vis[v]=Time; q.push(v); } }return len; } void solve() { int i; for(s=1,bfs(s,++num),i=1;i<=n;i++) if(g[i]>g[s]) s=i; for(bfs(s,++num),i=1;i<=n;i++) if(g[i]>g[t]) t=i; belong[n=1]=t;num++; do{ t=fa[t],belong[++n]=t,vis[t]=num; }while(t!=s); for(i=1;i<=n;i++) f[i]=g[belong[1]]-g[belong[i]]; for(i=1;i<=n;i++) tmp[i]=g[belong[i]]; for(i=1;i<=n;i++) h[i]=bfs(belong[i],num); for(i=1;i< n;i++) g[i]=tmp[i];g[n]=0; } int main() { int x,y,z; n=read();m=read(); for(int i=1;i<n;i++) { x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } solve(); int l=1,r=0;ans=inf; for(l=1;l<=n;l++) { while(r<n && f[r+1]-f[l]<=m) { ++r;while(L<=R && que[R]<h[r]) R--; que[++R]=h[r];id[R]=r; } ans=min(ans,max(max(f[l],g[r]),que[L])); if(id[L]<=l)L++; } printf("%d\n",ans); return 0; }

[SDOI2011]消防(單調隊列,樹的直徑,雙指針)