点分治练习:不虚就是要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:牛客推荐系统开发之标签重复度(点分治+动态开点权值线段树)