点分治练习:不虚就是要AK

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点分治练习:不虚就是要AK相关的知识,希望对你有一定的参考价值。

 

【题面】

不虚就是要AK(czyak.c/.cpp/.pas)

  2s 128M

czy很火。因为又有人说他虚了。为了证明他不虚,他决定要在这次比赛AK。

现在他正在和别人玩一个游戏:在一棵树上随机取两个点,如果这两个点的距离是4的倍数,那么算czy赢,否则对方赢。现在czy想知道他能获胜的概率。

以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。

本题多组数据。对于每组数据:第一行一个数n,表示树上的节点个数 接下来n-1条边a,b,c描述a到b有一条长度为c的路径 当n=0时表示读入结束

数据组数不超过10

输入数据

5

1 2 1

1 3 2

1 4 1

2 5 3

0

输出数据

7/25

数据范围

数据点 n的规模 数据组数 随机生成数据
1 200 1
2 200 1
3 200 <=3
4 2000 <=3
5 2000 <=3
6 2000 <=5
7 20000 <=5
8 20000 <=5
9 20000 <=10
10 20000 <=10

 

【思路】

       考虑过树根的情况。

       设sum[i]表示前S-1棵子树中dis%4=i的点数,tmp代表当前S子树。

       一遍dfs求出dis后累计答案即可。

       需要注意的是点对算两次而且路长0算作4的倍数。

 

【代码】

 

 1 #include<cstdio>
 2 #include<vector>
 3 #include<queue>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 8 using namespace std;
 9 
10 const int N = 20000+10;
11 
12 struct Edge {
13     int v,w;
14     Edge(int v=0,int w=0) :v(v),w(w){}
15 };
16 vector<Edge> g[N];
17 int n,m,k,ans;
18 int root,size,siz[N],dis[N],f[N],vis[N];
19 
20 int gcd(int x,int y) { return y==0? x:gcd(y,x%y); }
21 
22 void getroot(int u,int fa) {
23     siz[u]=1; f[u]=0;
24     for(int i=0;i<g[u].size();i++) {
25         int v=g[u][i].v;
26         if(v!=fa && !vis[v]) {
27             getroot(v,u);
28             siz[u]+=siz[v];
29             if(siz[v]>f[u]) f[u]=siz[v];
30         }
31     }
32     f[u]=max(f[u],size-siz[u]);
33     if(f[u]<f[root]) root=u;
34 }
35 int tmp[4],sum[4];
36 void dfs(int u,int fa) {
37     tmp[dis[u]]++;
38     for(int i=0;i<g[u].size();i++) {
39         int v=g[u][i].v;
40         if(v!=fa && !vis[v]) {
41             dis[v]=(dis[u]+g[u][i].w)%4;
42             dfs(v,u);
43         }
44     }
45 }
46 void solve(int u) {
47     memset(sum,0,sizeof(sum));
48     vis[u]=1; sum[0]=1;
49     for(int i=0;i<g[u].size();i++) {
50         int v=g[u][i].v;
51         if(!vis[v]) {
52             dis[v]=g[u][i].w%4;
53             dfs(v,u);
54             for(int j=0;j<4;j++)
55                 ans+=tmp[j]*sum[(4-j)%4];
56             for(int j=0;j<4;j++)
57                 sum[j]+=tmp[j],tmp[j]=0;
58         }
59     }
60     for(int i=0;i<g[u].size();i++) {
61         int v=g[u][i].v;
62         if(!vis[v]) {
63             size=siz[v]; root=0;
64             getroot(v,-1); solve(root);
65         }
66     }
67 }
68 void read(int& x) {
69     char c=getchar(); int f=1; x=0;
70     while(!isdigit(c)){if(c==-) c=-1; c=getchar();}
71     while(isdigit(c)) x=x*10+c-0,c=getchar();
72     x*=f;
73 }
74 int main() {
75     //freopen("in.in","r",stdin);
76     //freopen("out.out","w",stdout);
77     while(read(n),n!=0) {
78         ans=0;
79         FOR(i,0,n) g[i].clear();
80         memset(vis,0,sizeof(vis));
81         int u,v,w;
82             FOR(i,1,n-1) {
83             read(u),read(v),read(w);
84             g[u].push_back(Edge(v,w));
85             g[v].push_back(Edge(u,w));
86         }
87         root=0; f[0]=1e9; size=n;
88         getroot(1,-1) , solve(root);
89         int b=n*n; ans=ans*2+n;
90         int gc=gcd(ans,b);
91         printf("%d/%d\n",ans/gc,b/gc);
92     }
93     return 0;
94 }

 

以上是关于点分治练习:不虚就是要AK的主要内容,如果未能解决你的问题,请参考以下文章

牛客练习赛84:牛客推荐系统开发之标签重复度(点分治+动态开点权值线段树)

BZOJ1468 点分治入门练习题

点分治练习: boatherds

牛客练习赛11 B trie树+拓扑判环 E 分治求平面最近点对

牛客练习赛81 D.小 Q 与树(点分治+容斥)

分治算法用于在数组中查找峰值。