北京理工大学小学期乐学 t23树上统计
寫在前頭
玩一下csdn的markdown。
這道題的大意是
給定一棵樹,兩個相鄰點之間的距離為111,對于相鄰距離為222的點之間可以新建一條邊,要求每個點到其他所有點點的距離之和的和。
看似是一道搜索題,但是我多日后再想起來感覺還有動態規劃的思路在里頭。
t23
參考帖子:http://lexue.bit.edu.cn/mod/forum/discuss.php?d=126124,閱讀前請先看一看這篇文章。
如果看不到原帖,這里大致概括本貼中用到的原帖中的結論,對于相鄰的節點 i,ji,ji,j,原帖總結了所有節點中除了 i,ji,ji,j 外的任意節點 xxx 到 iii 的距離 ddd 于到 jjj 的距離 d′d'd′ 之間的關系。
本文是對上貼中最后留白部分的推論,我寫這個的時候程序還沒寫(主要是擔心寫不出ac,先寫寫推導過程拿一點平時分orz)
約定
在zzxdl的帖子中兩點之間的距離用 distdistdist 表示,文中用 ddd 表示,且 d(x,x)=0,d′(x,x)=0d(x,x)=0,d'(x,x)=0d(x,x)=0,d′(x,x)=0
正文
對于圖 G(E,V)G(E,V)G(E,V),取一條邊 i?ji-ji?j,邊上的兩點分別為 i,ji,ji,j,設節點 iii 的所有子樹編號為 1,…,n1,\ldots,n1,…,n,jjj 節點所有子樹編號為1,…,m1,\ldots,m1,…,m,對于 iii 節點的第 kkk 個子樹上所有節點的集合為 SikS_{ik}Sik?,其中所有到 iii 距離為偶數的節點的集合為 EikE_{ik}Eik? ,距離為奇數的節點的集合為 OikO_{ik}Oik?。于節點 jjj 同理。
那么有:
?kEjk=?kOik?{j}?kOjk=?kEik∪{i}\begin{aligned} \bigcup_k{E_{jk}}=\bigcup_k{O_{ik}}-\{j\}\\ \bigcup_k{O_{jk}}=\bigcup_k{E_{ik}}\cup\{i\}\\ \end{aligned} k??Ejk?=k??Oik??{j}k??Ojk?=k??Eik?∪{i}?
令:
Oi=?kOikOj=?kOjkEi=?kEikEj=?kEjksumi=∑u≠id′(u,i)sumj=∑u≠jd′(u,j)\begin{aligned} &O_i=\bigcup_k{O_{ik}} & O_j=\bigcup_k{O_{jk}}\\ &E_i=\bigcup_k{E_{ik}} & E_j=\bigcup_k{E_{jk}}\\ &sum_i=\sum_{u\neq i}{d'(u,i)}\\&sum_j=\sum_{u\neq j}{d'(u,j)} \end{aligned} ?Oi?=k??Oik?Ei?=k??Eik?sumi?=u?=i∑?d′(u,i)sumj?=u?=j∑?d′(u,j)?Oj?=k??Ojk?Ej?=k??Ejk?
顯然,結合上文的推導:
sumi=∑u∈Eid′(u,i)+∑u∈Oid′(u,i)sumj=∑u∈Ejd′(u,j)+∑u∈Ojd′(u,j)=∑u∈Oi?{j}d′(u,j)+∑u∈Ei+{i}d′(u,j)=∑u∈Oid′(u,j)+∑u∈Eid′(u,j)+1\begin{aligned} sum_i&=\sum_{u\in E_i}{d'(u,i)}+\sum_{u\in O_i}{d'(u,i)}\\ sum_j&=\sum_{u\in E_j}{d'(u,j)}+\sum_{u\in O_j}{d'(u,j)}\\ &=\sum_{u\in O_i-\{j\}}{d'(u,j)}+\sum_{u\in E_i+\{i\}}{d'(u,j)}\\ &=\sum_{u\in O_i}{d'(u,j)}+\sum_{u\in E_i}{d'(u,j)}+1\\ \end{aligned} sumi?sumj??=u∈Ei?∑?d′(u,i)+u∈Oi?∑?d′(u,i)=u∈Ej?∑?d′(u,j)+u∈Oj?∑?d′(u,j)=u∈Oi??{j}∑?d′(u,j)+u∈Ei?+{i}∑?d′(u,j)=u∈Oi?∑?d′(u,j)+u∈Ei?∑?d′(u,j)+1?
由于 (i,j)∈E(i,j)\in E(i,j)∈E,所以有且僅有唯一的 p∈{1,2…,n}p\in\{1,2\ldots,n\}p∈{1,2…,n},使得其所對應的子樹中所有點的集合滿足:
j∈SipEip+Oip=Sip\begin{aligned} j&\in S_{ip}\\ E_{ip}+O_{ip}&=S_{ip} \end{aligned} jEip?+Oip??∈Sip?=Sip??
那么:
sumj=∑u∈Oi?Oipd′(u,j)+∑u∈Ei?Eipd′(u,j)+1+∑u∈Oipd′(u,j)+∑u∈Eipd′(u,j)=∑u∈Oi?Oipd′(u,i)+∑u∈Ei?Eipd′(u,i)+1+∣Ei∣?∣Eip∣+∑u∈Oipd′(u,i)?∣Oip∣+∑u∈Eipd′(u,i)=∑u∈Eid′(u,i)+∑u∈Oid′(u,i)+∣Ei∣?∣Sip∣+1=sumi+∣Ei∣?∣Sip∣+1\begin{aligned} sum_j&=\sum_{u\in O_i-O_{ip}}{d'(u,j)}+\sum_{u\in E_i-E_{ip}}{d'(u,j)}+1+\sum_{u\in O_{ip}}{d'(u,j)}+\sum_{u\in E_{ip}}{d'(u,j)}\\ &=\sum_{u\in O_i-O_{ip}}{d'(u,i)}+\sum_{u\in E_i-E_{ip}}{d'(u,i)}+1+|E_i|-|E_{ip}|+\sum_{u\in O_{ip}}{d'(u,i)}-|O_{ip}|+\sum_{u\in E_{ip}}{d'(u,i)}\\ &=\sum_{u\in E_i}{d'(u,i)}+\sum_{u\in O_i}{d'(u,i)}+|E_i|-|S_{ip}|+1\\ &=sum_i+|E_i|-|S_{ip}|+1 \end{aligned} sumj??=u∈Oi??Oip?∑?d′(u,j)+u∈Ei??Eip?∑?d′(u,j)+1+u∈Oip?∑?d′(u,j)+u∈Eip?∑?d′(u,j)=u∈Oi??Oip?∑?d′(u,i)+u∈Ei??Eip?∑?d′(u,i)+1+∣Ei?∣?∣Eip?∣+u∈Oip?∑?d′(u,i)?∣Oip?∣+u∈Eip?∑?d′(u,i)=u∈Ei?∑?d′(u,i)+u∈Oi?∑?d′(u,i)+∣Ei?∣?∣Sip?∣+1=sumi?+∣Ei?∣?∣Sip?∣+1?
由此再進行一次深度優先搜索即可得到答案。
貼上代碼
#include <iostream> #include <vector> using namespace std;#define N 200 // 2000000 #define HALF(x) (((x)&1) + ((x)>>1)) // 除二并向上取整vector<int> edge[N]; /* oddR 所有節點到根節點的奇數點的個數 evenR 所有節點到根節點的偶數點的個數 sizeTree 以r為根的數中每一個幾點所代表的子樹的節點個數 */ int oddR, evenR, sizeTree[N]; int disRoot[N]; // 節點到根的距離 long long sum[N], sumUp;int E(int i); // 到節點i的距離為偶數的點的個數 int O(int i); // 到節點i的距離為奇數的點的個數 int dfs1(int i, int f); // 給出所有點的子樹的規模 void dfs2(int i, int f, int r, int depth); // 給出所有點的奇偶節點的數量 void dfs3(int i, int f); // 轉移計算int main(void) {int n, a, b, r;cin >> n;while (n-- > 1){scanf("%d%d", &a, &b); getchar();edge[a].push_back(b);edge[b].push_back(a);}r = a;dfs1(r, 0);dfs2(r, 0, r, 1);sumUp += sum[r];dfs3(r, 0);cout << sumUp / 2 << endl;return 0; }void dfs3(int i, int f) {for (int j = 0; j < edge[i].size(); j++){int tmp = edge[i][j];if (tmp != f){sum[tmp] = sum[i] + E(i) - sizeTree[tmp] + 1;sumUp += sum[tmp];dfs3(tmp, i);}}return; }int E(int i) { return disRoot[i] % 2 == 0 ? evenR : oddR - 1; } int O(int i) { return disRoot[i] % 2 == 0 ? oddR : evenR + 1; }void dfs2(int i, int f, int r, int depth) {for (int j = 0; j < edge[i].size(); j++){int tmp = edge[i][j];if (tmp != f){if (depth & 1 == 1) // 距離根節點奇數距離oddR++;else evenR++; // 距離根節點偶數距離disRoot[tmp] = depth;sum[r] += HALF(depth);dfs2(tmp, i, r, depth + 1);}}return; }int dfs1(int i, int f) {for (int j = 0; j < edge[i].size(); j++){int tmp = edge[i][j];if (tmp != f)sizeTree[i] += dfs1(tmp, i);}sizeTree[i]++;return sizeTree[i]; }總結
以上是生活随笔為你收集整理的北京理工大学小学期乐学 t23树上统计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 北京理工大学c语言作业三做一年级算术题,
- 下一篇: 北理工-大二数据结构乐学编程题-约瑟夫问