(写篇博客证明自己还活着×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 MB

Description

聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画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

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。
 
拿到本题,一个显然的思路是统计长度为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 }
其中$size[i]$表示目前以i点为根的子树size大小 $maxs[i]$为i点size最大的儿子的size(这个“儿子”可以是i点在有根树中的父亲)
$totsize$为当前联通块大小 $root$为最终的重心 $vis[i]$表示i点是否已经被选取作为重心。vis保证我们可以“截取”出准确的对应联通块然后是找重心的部分:
 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 }
这其实类似一个树规的$O(n)$过程……这样找到最后$root$就是重心然后是solve过程:
 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 }
我们在每一次确定的重心上进行计算,calc函数的内容随题目而变,但是大概是长这样的……本题的完整代码:
 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 }
BZOJ2152

相关文章: