題意:一棵n個點的樹,每條邊有距離v,求該樹中距離小於等於k的點的對數。
dis[y]表示點y到根x的距離,v代表根到子樹根的距離;
那麼不在同一棵子樹中的兩點i、j之間的距離為dis[i]+dis[j]
; ①
設得到這個距離的時間複雜度為o(w);
如果我們層層如此遞迴即可得到所有的點對數量,可以證明複雜度為o(logn*w);
因為n的範圍為(n<=10000)所以我們需要w與n近似;
那麼此時問題轉化為了如何在大約為o(n)的複雜度內得到①;
乙個個計算不在同一子樹中顯然是麻煩的,如果選擇先計算整棵樹的點對數量然後去掉重複計數的點對數問題就可以得到簡化;
如果只是計算一棵樹下符合條件dis[j]+dis[i]<=k的點對數量,我們將距離sort,很容易在 log(樹的大小) 的複雜度下把問題解決,再用幾乎同樣的時間減去每一棵子樹中符合dis[j]+dis[i]+2*v<=k的點對的數量就可以得到答案。
那麼總時間複雜度為o(n*logn*logn);
當然這只是理想情況,如果這棵樹退化為一條鏈,複雜度則會變為o(n*n*logn)顯然是超時的;
所以在每次遞迴前o(n)的複雜度找一下樹的重心,
**如下
1 #include2 #include3 #include4 #include5 #include6view code//using namespace std;
7const
int maxn=10010;8
const
double eps=1e-8;9
const
int modn=45989;10
intn,k;
11struct
node[maxn*2
];15
int head[maxn]={},siz[maxn]={},ma[maxn]={},dis[maxn]={};
16int tot=0,tot1,root=0
,now,ans;
17bool vis[maxn]={};
18void init(int x,int y,int
v)24
void getsiz(int x,int fa)35}
36}37void cen(int r,int x,int fa)
42if(ma[x]
45for(int i=head[x];i;i=e[i].next)50}
51}52void getdis(int x,int fa,int di)60}
61}62int sum(int x,int d)
72 cnt+=j-i;
73 i++;74}
75return
cnt;76}
77void dfs(int
x)90}91
}92void
yu()
97int
main()
106 dfs(1
);107 printf("
%d\n
",ans);
108}
109return0;
110 }
poj 1741 Tree 樹的分治
解 文中有,就是把路徑分成經過樹根,和不經過樹根兩類,每次處理經過樹根的,然後剩下的子樹部分可以遞迴處理,如果每次選取樹的重心,那麼可以保證最多遞迴logn層。經過樹根的先排序,然後o n 的複雜度就能算出,那麼每層的複雜度nlogn,最多遞迴logn層,總複雜度nlogn 2 include in...
poj 1741 Tree 樹的分治
求樹上倆結點之間距離小於k的結點對個數 倆個點a,b的公共祖先為c,那麼這倆個點的距離就可以用dis a,c dis b,c 表示,我們可以不斷的處理這樣c,然後計算距離,處理點對,為了使平均效能最好,我們需要找的c為每一棵子樹的重心 需要注意的算點對的時候這種方法成立的條件是倆個點不在同一顆子樹上...
POJ 1741 Tree 樹的分治
題目 題意 給定一棵樹,有n個點和n 1條邊,邊有邊權,給出乙個k,求樹上任意兩點間的最短距離不大於k的個數 思路 看了09年漆子超的 利用分治法,每次找樹的重心,重心即是在當前樹中刪掉此點後,節點數最多的子樹的節點數最小,找到重心後,求其所有子孫到其的距離,然後統計一下兩兩之和不大於k的個數,另外...