NOIP 2014 Senior 2 聯合權值

2021-09-30 13:58:19 字數 3664 閱讀 3871

思路1

很明顯,題目給定的是一棵樹,所以想到使用樹形dp,所以首先,將無根樹轉換成有根樹。可以記錄乙個結點的所有孫結點(子結點的子結點)的最大權值以及它們的權值和(當然要求餘了)。對於某個結點和它的所有孫子結點來說,它們的最大聯合權值就是孫子結點的最大權值乘以該結點的權值,聯合權值和就是孫子結點與該結點的權值之積的和,再乘以2。對於求聯合權值之和,可以用乘法分配律合併,變成求孫子結點權值之和與該結點的權值的積。

可以說這是樹形dp的常規思路:從下往上更新資訊。但這還不夠,因為有些點對沒有計算再內。不要忘了任意一棵子樹的所有(直系)子結點兩兩也成聯合點對。當然,這要求子結點至少有2個。對這些結點來說,求最大聯合權值需要記錄前兩大的權值,求聯合權值之和就稍微考了下效率:如果使用平方級時間複雜度的演算法,那麼將會超時。這裡仍然使用乘法分配律:對於一棵子樹的子結點兩兩形成的聯合點對,其權值和為σn

i=1(

σcos

t−co

sti)

(cos

ti)

。這樣,就能以線性時間複雜度求出和了。

參考**

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

std::cin;

using

std::cout;

using

std::endl;

typedef

int int;

inline int readin()

while (ch >= '0' && ch <= '9')

if (minus) a = -a;

return a;

}const

int maxn = 200005;

const int mod = 10007;

const

int root = 1;

int n;

std::vector

<:vector>

> edges;

int weight[maxn];

int parent[maxn];

int grand[maxn];

int fmax[maxn]; //孫子結點的最大權值

int fsum[maxn]; //孫子結點權值之和

int fcsum[maxn]; //子結點權值之和

int fcmajor[maxn]; //子結點最大權值

int fcminor[maxn]; //子結點次大權值

int maxans;

int sumans;

void dfs(int node = root)

else}}

if (grand[node]) //更新爺節點 = =

for (int i = 0; i < edges[node].size(); i++)

//更新在下面的

maxans = std::max(maxans, fmax[node] * weight[node]);

sumans += fsum[node] % mod * weight[node] % mod * 2; //記住乘以2

sumans %= mod;

//更新同級的

if (edges[node].size() - bool(parent[node]) >= 2) //需要至少有2個子結點

}}void run()

for (int i = 1; i <= n; i++)

dfs();

sumans %= mod;

cout

<< maxans << " "

<< sumans << endl;

}int main()

有沒有覺得很麻煩,隨時要超時的感覺?事實上,如果題目給出極端資料:一條20萬個結點的鏈,這個程式將直接爆棧。在windows中最多能遞迴約3萬層(即使只傳乙個引數也是這樣,所以不把parent和grand當作引數傳入的原因是因為我在測試),而在noi linux中也僅能遞迴約13萬層(以上資料不保證準確)。還好資料中的層數都很少,僥倖全過。

這種演算法麻煩的原因就在於:你能想到最後還要單獨更新子結點,你就不能直接用這種方法更新所有結點嗎?所以就有了好寫好懂好過的思路2。

思路2

列舉每個結點,與結點i相鄰的結點一定兩兩成聯合點對,並且列舉每個結點時不會出現重複的聯合點對,當然也不會遺漏。求題目中的兩個問題的方法就是思路1中求解子結點的兩個問題的方法。

參考**

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

std::cin;

using

std::cout;

using

std::endl;

typedef

int int;

inline int readin()

while (ch >= '0' && ch <= '9')

if (minus) a = -a;

return a;

}const

int maxn = 200005;

const int mod = 10007;

int n;

std::vector

<:vector>

> edges;

int weight[maxn];

int maxans;

int sumans;

void run()

for (int i = 1; i <= n; i++)

for (int i = 1; i <= n; i++)

else

}isum += weight[edges[i][j]];

isum %= mod;

}maxans = std::max(maxans, imajor * iminor);

for (int j = 0; j < edges[i].size(); j++)

}cout

<< maxans << " "

<< sumans << endl;

}int main()

總的來說,這道題看了思路2後。。。真的一點也不複雜,就考考你知不知道樹。所以說啊。。。還是太弱,有些時候經常在危險的時間上徘徊,只要稍加分析,就能得到更優的演算法。

另外,思路一當初我只想到了第一步,第二步還是我在測試時發現的,看來寫完後檢查真的很重要啊!

Noip2014senior複賽 飛揚的小鳥

noip2014senior複賽 飛揚的小鳥 問題描述 1.遊戲介面是乙個長為 n,高為 m 的二維平面,其中有 k 個管道 忽略管道的寬度 2.小鳥始終在遊戲介面內移動。小鳥從遊戲介面最左邊 任意整數高度位置出發,到達遊戲介面最右邊時,遊戲完成。3.小鳥每個單位時間沿橫座標方向右移的距離為 1,豎...

NOIP 2012 Senior 3 開車旅行

還是太弱了 這道題拿到手上後完全沒有能夠在規定時間內解決的思路。不過還好,大體思路是對的 首先預處理a b在每個地方開一次車到達的地方。對於第一問,列舉a出發的位置,對於第二問,直接計算就行了。計算的方法就是挨著推,直到滿足題目中結束旅行的條件。很明顯,預處理的時間複雜度為o n2 計算的時間複雜度...

noip2014 d2解題報告

d2也挺簡單的,t1乙個二維字首和,t2乙個bfs spfa,t3難度較大,但是 實現難度不大,而且30非常好拿。所以兩天成績 260 230 490,好菜啊qwq,往年的題都做成這個樣子 t1 無線網路發射器選址,又是乙個模擬,只要處理下二維字首和就好,注意判斷好邊界,不要讓陣列越界 includ...