【BZOJ2152】聪聪可可,dfs+递推/点分治
2016-04-26 14:39
253 查看
传送门
写在前面:╯﹏╰
思路:刚开始char哥在做这个题,提供了不用点分治而是树上递推的做法,然后我就先调出来了……
对树进行dfs,f[u][y]表示以x为根的子树中,与u距离为y的点(mod 3),g[u]为以x为根的子树中符合条件的点对(不包含(1,1),(2,2)……),那么最后答案就是((g[1]+n)/gcd,n∗n/gcd)
f的递推式还是比较好想的
f[u][w(u,v)+y]=∑f[v][y]v为x的儿子
g的递推式有些细节,首先在统计3种点对(在mod3情况下为0,1,2)时,分成LCA为u的点对和LCA不为u的点对(实际上就是一棵子树中原本有的点对和两个子树间的点对)
前者直接加g[v]就好了
后者则是利用乘法原理,比如第一棵子树中与u距离为1(mod 3)的点有3个,其他子树(不能包含第一棵子树中的点,因为已经在dfs第一棵子树的时候统计过了,我们要求的是LCA不为u的点对!)中与u距离为2的点有2个,那么g[u]+=2*3,同时值得注意的是,在计算距离为0的点时,u也是要算进去的,但计算一次子树时它只能算一次(好可怜啊),其他点在循环后都算了两次的,所以我们计算时要加个一
注意:都说过了……
代码:
附样例
在没学过一个算法时,我们并不要畏惧它,甚至连看都不敢看(至少想想如果在考场上遇到,暴力该怎么打吧……)另辟蹊径,也能走出一片天地
写在前面:╯﹏╰
思路:刚开始char哥在做这个题,提供了不用点分治而是树上递推的做法,然后我就先调出来了……
对树进行dfs,f[u][y]表示以x为根的子树中,与u距离为y的点(mod 3),g[u]为以x为根的子树中符合条件的点对(不包含(1,1),(2,2)……),那么最后答案就是((g[1]+n)/gcd,n∗n/gcd)
f的递推式还是比较好想的
f[u][w(u,v)+y]=∑f[v][y]v为x的儿子
g的递推式有些细节,首先在统计3种点对(在mod3情况下为0,1,2)时,分成LCA为u的点对和LCA不为u的点对(实际上就是一棵子树中原本有的点对和两个子树间的点对)
前者直接加g[v]就好了
后者则是利用乘法原理,比如第一棵子树中与u距离为1(mod 3)的点有3个,其他子树(不能包含第一棵子树中的点,因为已经在dfs第一棵子树的时候统计过了,我们要求的是LCA不为u的点对!)中与u距离为2的点有2个,那么g[u]+=2*3,同时值得注意的是,在计算距离为0的点时,u也是要算进去的,但计算一次子树时它只能算一次(好可怜啊),其他点在循环后都算了两次的,所以我们计算时要加个一
注意:都说过了……
代码:
#include<bits/stdc++.h> using namespace std; int tot,g[20010],f[20010][3],first[20010],n,x,y,z; int fa[20010]; struct edge { int u,v,w,next; }e[40010]; void add(int x,int y,int z) { e[++tot].u=x; e[tot].v=y; e[tot].w=z; e[tot].next=first[x]; first[x]=tot; } void dfs(int x) { for (int i=first[x];i;i=e[i].next) if (fa[x]!=e[i].v) { fa[e[i].v]=x; dfs(e[i].v); f[x][e[i].w]+=f[e[i].v][0]; f[x][(e[i].w+1)%3]+=f[e[i].v][1]; f[x][(e[i].w+2)%3]+=f[e[i].v][2]; } for (int i=first[x];i;i=e[i].next) if (fa[x]!=e[i].v) { g[x]+=g[e[i].v]; g[x]+=(f[x][0]-f[e[i].v][(3-e[i].w)%3]+1)*f[e[i].v][(3-e[i].w)%3];//这就是我们刚才说的问题,(u,x)和(x,u)都是要算的 g[x]+=(f[x][1]-f[e[i].v][(4-e[i].w)%3])*f[e[i].v][(5-e[i].w)%3]; g[x]+=(f[x][2]-f[e[i].v][(5-e[i].w)%3])*f[e[i].v][(4-e[i].w)%3]; } } int gcd(int x,int y) { if (!y) return x; return gcd(y,x%y); } main() { scanf("%d",&n); for (int i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z), z%=3,add(x,y,z),add(y,x,z); for (int i=1;i<=n;i++) f[i][0]=1; dfs(1); int p=gcd(g[1]+n,n*n); printf("%d/%d",(g[1]+n)/p,n*n/p); }
附样例
6 1 2 3 1 6 4 3 1 7 4 3 6 3 5 5 7/18 9 1 2 1 1 3 2 2 4 1 2 5 2 4 6 1 4 7 2 4 8 0 3 9 1 11/27 5 1 2 1 1 3 2 2 4 1 2 5 2 11/25 3 1 2 0 2 3 0 1/1
在没学过一个算法时,我们并不要畏惧它,甚至连看都不敢看(至少想想如果在考场上遇到,暴力该怎么打吧……)另辟蹊径,也能走出一片天地
相关文章推荐
- Mysql rpm包安装
- MT4客户端通讯分析(一)——登录部分分析
- 如何让APP在最短的时间内成功上线?
- 解决Scrapy性能问题——案例一(CPU饱和)
- cnn中权值共享理解
- 防火墙学习随笔
- Visual Studio2010简体中文版/旗舰版安装教程
- jq中的ajax
- POJ 1830 开关问题 (01高斯消元)
- UNPv1第二十六章:数据链路访问
- CentOS安装jdk1.8 及服务器之间的拷贝
- 两天 写出简易数据库管理程序
- 以“不变应万变”,我们需要怎么做?
- androidstudio编译的时候报R.java类重复错误怎么搞
- solr源码入门1
- 构架相关
- 软件企业测试团队的组织架构
- Ubuntu16.04升级之后VirtualBox不能安装的问题
- 用户管理
- Cocos2d-x lua 屏幕适配