【HDU - 5886】Tower Defence(树的直径,思维,dp)
題干:
There was a civil war between two factions in Skyrim, a province of the Empire on the continent of Tamriel. The Stormcloaks, led by Ulfric Stormcloak, are made up of Skyrim's native Nord race. Their goal is an independent Skyrim free from Imperial interference. The Imperial Legion, led by General Tullius, is the military of the Empire that opposes the Stormcloaks and seeks to reunite and pacify the province.?
The current target of General Tullius is to defend Whiterun City. Near by this city there are?NN?towers under the Empire's control. There are?N?1N?1?roads link these tower, so solders can move from any tower to another one through these roads.?
In military affairs, tactical depth means the longest path between two towers of all. Larger the tactical depth is, more stable these towers are.?
According to the message sent by spies, General Tullius believe that Stormcloaks is planning to attack one of these roads, and his towers would be divided into two parts. However, Tullius does not know which one, so he supposes the possibility that Stormcloaks attack these roads are the same. Now, General Tullius ask for your help, to calculate the expectation of tactical depth after this attack.?
To avoid the issue of precision, you need to calculate?expectationoftacticaldepth×(N?1)expectationoftacticaldepth×(N?1).
Input
The first line of input contains an integer?tt, the number of test cases.?tt?test cases follow.?
For each test case, in the first line there is an integer?N(N≤100000)N(N≤100000).?
The?ii-th line of the next?N?1N?1?lines describes the?ii-th edge. Three integers?u,v,w?(0≤w≤1000)u,v,w?(0≤w≤1000)?describe an edge between?uu?and?vv?of length?ww.
Output
For each test cases, output?expectationoftacticaldepth×(N?1)expectationoftacticaldepth×(N?1).
Sample Input
2 3 2 1 2 3 2 5 5 2 1 7 3 1 7 4 2 5 5 2 6Sample Output
7 63題目大意:
給出一棵樹,邊上有權值,現在毀掉任意一條邊,分成兩部分,求這兩部分中最遠的兩點距離期望,答案*(n-1)
一句話題意:
N個點的一棵帶權樹。切掉某條邊的價值為切后兩樹直徑中的最大值。求各個邊切掉后的價值和(共N-1項)。
解題報告:
說白了就是求最遠距離加成和?
對于一棵樹來說,兩點最遠的距離,考慮兩點:?
1.最長鏈沒有被拆開,那么答案就是最長鏈;?
2.最長鏈被拆開了,那么答案一定在最長鏈的端點到某個葉子結點上;?
第一點很顯然,關于第二點的證明很簡單,假設最長路不是最長鏈的端點到某個葉子結點,那么另一條更長的路徑會取代他成為樹的直徑,與已知矛盾。
有了這個結論,這個題就好做了,首先預處理出最長鏈,數組存下來,然后對于每個最長鏈上的結點預處理出它的權值最大的葉子結點(不在最長鏈上),注意這里的處理看似是n^2的,但是因為是一棵樹,所以其實每個點只會被掃到一遍所以是線性的。剩下的就是處理最長鏈左邊和右邊的拆開后的最大值了,左右掃一遍,兩個數組LR存下來,最后On掃一遍最長鏈,取LR中最大值得到答案。?
剛開始想在bfs的過程中就處理處nxt數組和nxtw數組,后來發現不行,因為你不能保證dis[v]>ans的時候更新的一定是最終選擇的路徑,所以還是需要從起點到終點dfs一次,在回溯的時候這條路徑一定是我們選擇的。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 2e5 + 5; struct Edge {int u,v;ll w;int ne; } e[MAX]; int head[MAX],tot,n;//記錄邊數 int path[MAX],nxt[MAX],top;//top記錄最長鏈上的頂點數 ll dis[MAX],nxtw[MAX],sum[MAX],val[MAX],L[MAX],R[MAX]; int flag[MAX]; void add(int u,int v,ll w) {e[++tot].u = u;e[tot].v = v;e[tot].w = w;e[tot].ne = head[u];head[u] = tot; } int bfs(int st,ll& ans) {queue<int> q;int retp = st;ans = 0;//注意初始化 for(int i = 1; i<=n; i++) dis[i] = -1,nxt[i]=-1;q.push(st);dis[st] = 0; while(!q.empty()) {int cur = q.front();q.pop();for(int i = head[cur]; ~i; i = e[i].ne) {int v = e[i].v;if(dis[v] != -1) continue;q.push(v);dis[v] = dis[cur] + e[i].w;if(dis[v] >= ans) {ans = dis[v];retp = v;}}}return retp; } int st,ed; bool ok; void dfs(int cur,int fa) {if(cur == ed) {ok = 1;return;}for(int i = head[cur]; ~i; i = e[i].ne) {int v = e[i].v;if(v == fa) continue;dfs(v,cur);if(ok == 1) {nxt[cur] = v;nxtw[cur] = e[i].w;return;}} } ll dfs2(int cur,int fa) {ll res = 0;for(int i = head[cur]; ~i; i = e[i].ne) {int v = e[i].v;if(flag[v] == 1 || v == fa) continue;res = max(res,dfs2(v,cur) + e[i].w);}return res; } int main() {int t;cin>>t;while(t--) {scanf("%d",&n);//inittot=top=0;ok=0;for(int i = 1; i<=n; i++) head[i] = -1,sum[i] = 0,val[i]=L[i]=R[i]=0,flag[i] = 0;for(int a,b,i = 1; i<=n-1; i++) {ll c;scanf("%d%d%lld",&a,&b,&c);add(a,b,c);add(b,a,c);}ll maxx,ans=0;st = bfs(1,maxx); ed = bfs(st,maxx);dfs(st,-1);for(int i = st; i!=-1; i = nxt[i]) {flag[i] = 1;path[++top] = i;}ans = maxx * (n-top);for(int i = 1; i<top; i++) {//枚舉他和他兒子的那條邊 sum[i+1] = sum[i] + nxtw[path[i]];//照這么來看的話還是遞歸的寫法會好一些,因為正好就是兒子節點代表兒子到父親的那條邊 }for(int i = 1; i<=top; i++) {//搜索中間點 (st和ed也可以搜,只不過搜出來結果肯定是0)val[i] = dfs2(path[i],-1);}for(int i = 1; i<=top; i++) {//枚舉割最長鏈上的每一條邊 L[i] = max(val[i] + sum[i],L[i-1]);}for(int i = top; i>=1; i--) {R[i] = max(R[i+1],val[i] + (sum[top]-sum[i]));}for(int i = 1; i<top; i++) {ans += max(L[i],R[i+1]);}printf("%lld\n",ans);}return 0 ; }?
總結
以上是生活随笔為你收集整理的【HDU - 5886】Tower Defence(树的直径,思维,dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信用卡申请软件有哪些 省心管理再也不担心
- 下一篇: 银行理财不香了?新增规模和收益都被基金碾