(写篇博客证明自己还活着×2)
转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/8006488.html
有的时候,我们会发现这样一类题:它长得很像一个$O(n)$的树规,
但是却很难用单独的数组维护对应的信息,这样我们就有了淀粉质点分治。
通过直接统计($O(nlogn)$)或者加上数据结构(比如树状数组,堆,线段树等等)维护信息($O(nlog^{2}n)$),
我们可以统计之前不好统计的东西
这篇博客将会介绍点分治以及动态点分治的简单应用,希望阅读本文的你能有所收获,那就再好不过了。
一.树规到点分治
我们先来看一道简单的题目……
2152: 聪聪可可
Time Limit: 3 Sec Memory Limit: 259 MBDescription
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
Input
输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。
Output
以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。
Sample Input
5
1 2 1
1 3 2
1 4 1
2 5 3
1 2 1
1 3 2
1 4 1
2 5 3
Sample Output
13/25
【样例说明】
13组点对分别是(1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3) (4,4) (5,2) (5,3) (5,5)。
【数据规模】
对于100%的数据,n<=20000。
【样例说明】
13组点对分别是(1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3) (4,4) (5,2) (5,3) (5,5)。
【数据规模】
对于100%的数据,n<=20000。
拿到本题,一个显然的思路是统计长度为3的倍数的路径个数,再比上总的路径个数即是答案。
那么我们发现,3的倍数%3=0(废话……),因此我们可以设$f[i][0/1/2]$为以i为根的子树,向下伸出的路径中长度%3=0/1/2的路径数。
这样我们在每一个点简单的合并一下(注意去掉同一子树的贡献)即可。
这样的做法显然是$O(n)$的对吧……
现在让我们考虑一个蠢一点的做法:
我们依然考虑每个点作为路径顶点时符合条件的点的个数,那么我们可以考虑它的每个子树,
数一下里面每个路径的条数,然后合起来对吧。
对于这个节点子树中的节点,我们是化作子问题递归处理的。
那么我们可以把树换个形状……换一个顺序来递归处理
由于重心有个性质:到其他点的距离和最小,
那我们可以暴力数一下每种长度0/1/2的路径有多少,再暴力加上,然后对于被这个重心分割出的几个子树分治递归解决……
这样找重心的复杂度呢?我们最多找$log$次重心就会只剩下一个点,因此复杂度为$O(nlogn)$.(因为每次找到一个重心,新的子树最多是原来子树大小的一半)
那么介绍一下我们必须的操作:
首先是初始化以及数组定义。我的习惯是这样的:
1 int size[N],maxs[N],totsize,root; 2 bool vis[N]; 3 inline void intn() 4 { 5 maxs[0]=inf,root=0,dfs1(1,0),solve(root); 6 }
$totsize$为当前联通块大小 $root$为最终的重心 $vis[i]$表示i点是否已经被选取作为重心。vis保证我们可以“截取”出准确的对应联通块然后是找重心的部分:
这其实类似一个树规的$O(n)$过程……这样找到最后$root$就是重心。然后是solve过程:
我们在每一次确定的重心上进行计算,calc函数的内容随题目而变,但是大概是长这样的……本题的完整代码:
BZOJ2152
1 inline void dfs1(int rt,int fa) 2 { 3 size[rt]=1,maxsize[rt]=0; 4 for(int i=adj[rt];i;i=s[i].next) 5 if(s[i].zhong!=fa&&!mark[s[i].zhong]) 6 { 7 dfs1(s[i].zhong,rt), 8 size[rt]+=size[s[i].zhong], 9 maxsize[rt]=max(maxsize[rt],size[s[i].zhong]); 10 } 11 maxsize[rt]=max(maxsize[rt],totsize-maxsize[rt]); 12 if(maxsize[rt]<maxsize[root])root=rt; 13 }
1 inline void solve(int rt) 2 { 3 vis[rt]=1; 4 /* 5 deal with ans 6 */ 7 for(int i=adj[rt];i;i=s[i].next) 8 if(!vis[s[i].zhong]) 9 totsize=size[s[i].zhong],root=0, 10 dfs1(s[i].zhong,0),solve(root); 11 }
1 #include <cstdio> 2 #include <cstring> 3 #define N 20010 4 #define inf 0x7fffffff 5 #define max(a,b) ((a)>(b)?(a):(b)) 6 int n,e,adj[N],root,totsize,size[N],maxsize[N]; 7 struct edge{int zhong,val,next;}s[N<<1]; 8 inline void add(int qi,int zhong,int val) 9 {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;s[e].val=val;} 10 bool mark[N]; 11 inline void dfs1(int rt,int fa) 12 { 13 size[rt]=1,maxsize[rt]=0; 14 for(int i=adj[rt];i;i=s[i].next) 15 if(!mark[s[i].zhong]&&s[i].zhong!=fa) 16 dfs1(s[i].zhong,rt),size[rt]+=size[s[i].zhong], 17 maxsize[rt]=max(maxsize[rt],size[s[i].zhong]); 18 maxsize[rt]=max(maxsize[rt],totsize-size[rt]); 19 if(maxsize[rt]<maxsize[root])root=rt; 20 } 21 int cnt[3],dist[N],ans; 22 inline void dfs2(int rt,int fa) 23 { 24 ++cnt[dist[rt]]; 25 for(int i=adj[rt];i;i=s[i].next) 26 if(!mark[s[i].zhong]&&s[i].zhong!=fa) 27 dist[s[i].zhong]=(dist[rt]+s[i].val)%3, 28 dfs2(s[i].zhong,rt); 29 } 30 inline int calc(int rt,int stval) 31 { 32 dist[rt]=stval,cnt[0]=cnt[1]=cnt[2]=0,dfs2(rt,0); 33 return cnt[0]*cnt[0]+2*cnt[1]*cnt[2]; 34 } 35 inline void solve(int rt) 36 { 37 mark[rt]=1,ans+=calc(rt,0); 38 for(int i=adj[rt];i;i=s[i].next) 39 if(!mark[s[i].zhong]) 40 ans-=calc(s[i].zhong,s[i].val), 41 totsize=size[s[i].zhong],root=0, 42 dfs1(s[i].zhong,0),solve(root); 43 } 44 inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);} 45 int main() 46 { 47 scanf("%d",&n); 48 register int i,a,b,c; 49 for(i=1;i<n;++i) 50 scanf("%d%d%d",&a,&b,&c),add(a,b,c%3),add(b,a,c%3); 51 totsize=n,root=0,maxsize[0]=inf; 52 dfs1(1,0),solve(root); 53 int di=n*n,d=gcd(di,ans); 54 printf("%d/%d\n",ans/d,di/d); 55 }