zoukankan      html  css  js  c++  java
  • LG2634 [国家集训队]聪聪可可

    题意

    题目描述

    聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。

    他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。

    聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。

    输入输出格式

    输入格式:

    输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。

    输出格式:

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

    输入输出样例

    输入样例#1: 复制
    5
    1 2 1
    1 3 2
    1 4 1
    2 5 3
    输出样例#1: 复制
    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余0,1,2的点的数量(t_0,t_1,t_2),然后答案就是(t_0∗(t_0-1)+t_0+2∗t_1∗t_2)

    这个公式是这么推出来的.
    每一条长度余2 和长度为1 的路径,就可以被统计一次
    又因为可以交换顺序 所以需要乘上2
    然后就是 所有的距离模3 已经为0 的点可以组成的路径.
    当然 直接到根结点距离为0 的需要单独拎出来.

    概率就是总方案数除以(n^2)

    时间复杂度(O(n log n))

    小众树形DP

    显然可以(f[u,0/1/2])表示子树到根的距离模3等于0/1/2的点的个数,然后用卷积统计答案。时间复杂度(O(n))

    代码

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read(){
        rg T data=0,w=1;rg char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
        while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
        return data*w;
    }
    template<class T>il T read(rg T&x) {return x=read<T>();}
    typedef long long ll;
    using namespace std;
    
    co int N=2e4+1;
    struct edge{int to,next,w;}a[N*2];
    int n,head[N],cnt,root,sum,vis[N],sz[N],f[N],dep[N],t[3],ans;
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    void getroot(int u,int fa){
    	sz[u]=1,f[u]=0;
    	for(int e=head[u],v;e;e=a[e].next){
    		if((v=a[e].to)==fa||vis[v]) continue;
    		getroot(v,u),sz[u]+=sz[v],f[u]=max(f[u],sz[v]);
    	}
    	f[u]=max(f[u],sum-sz[u]);
    	if(f[u]<f[root]) root=u;
    }
    void getdeep(int u,int fa){
    	++t[dep[u]%3];
    	for(int e=head[u],v;e;e=a[e].next){
    		if((v=a[e].to)==fa||vis[v]) continue;
    		dep[v]=dep[u]+a[e].w,getdeep(v,u);
    	}
    }
    int calc(int u,int d0){
    	dep[u]=d0,memset(t,0,sizeof t);
    	getdeep(u,0);
    	return t[0]*t[0]+2*t[1]*t[2];
    }
    void solve(int u){
    	ans+=calc(u,0),vis[u]=1;
    	for(int e=head[u],v;e;e=a[e].next){
    		if(vis[v=a[e].to]) continue;
    		ans-=calc(v,a[e].w);
    		sum=sz[v],root=0,getroot(v,0);
    		solve(root);
    	}
    }
    int main(){
    	read(n);
    	for(int i=1,u,v,w;i<n;++i){
    		read(u),read(v),read(w);
    		a[++cnt]=(edge){v,head[u],w},head[u]=cnt;
    		a[++cnt]=(edge){u,head[v],w},head[v]=cnt;
    	}
    	f[0]=sum=n,getroot(1,0);
    	solve(root);
    	int g=gcd(ans,n*n);
    	printf("%d/%d
    ",ans/g,n*n/g);
    	return 0;
    }
    
  • 相关阅读:
    进程池,线程池,协程,gevent模块,协程实现单线程服务端与多线程客户端通信,IO模型
    线程相关 GIL queue event 死锁与递归锁 信号量l
    生产者消费者模型 线程相关
    进程的开启方式 进程的join方法 进程间的内存隔离 其他相关方法 守护进程 互斥锁
    udp协议 及相关 利用tcp上传文件 socketserver服务
    socket套接字 tcp协议下的粘包处理
    常用模块的完善 random shutil shevle 三流 logging
    day 29 元类
    Django入门
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/autoint/p/10682767.html
Copyright © 2011-2022 走看看