【USACO 2013 open】【JZOJ 3234】阴阳
2016-06-13 20:50
302 查看
Description
Farmer John 正在在计划自己的农场漫步。他的农场的结构就像一棵树:农场有N个谷仓(1<= N <=100,000),分别由N-1条路链接。这样,他便可以通过这些谷仓间的道路遍及各个谷仓。Farmer John想要选择一条路线:这条路线的起点和终点分别为农场中两个不同的谷仓,这条路线不能重复经过一条边两次。Farmer John担心这条路径可能会偏长,所以他想在路线上寻找一个休息点(当然这个休息点不能为起点或者终点)。每条边的两旁都是牛群,要么是Charcolais(白毛),要么是Angus(黑毛)。Farmer John是一个聪明人,所以他想要在他通过小路的同时平衡小路两侧阴阳的力量。他要选择一条路径使得他从起点到休息站,和从休息站到终点这两段路上都满足路两边的Charcolais牛群和Angus牛群总数量相同。
Farmer John好奇他能找到多少条如上所述的平衡的路径。我们认为,当且仅当两条路线的边的集合不同时,这两条路径才被认为是不同的,否则认为是相同的路线。就算路线上有多个有效的“休息站”的位置能使路线平衡,我们也只记为一条路线。
请帮助计算有多少条不同的平衡路线。
Solution
很显然这是一道点分治,白毛为1,黑毛为-1,
我们每轮的点分治表示过重心的合法路径,(以下的距离指与根节点的路径和)
设Cs表示之前从根出发第一次遇到距离为s的点的个数,cs为之前从根出发非第一次(也就是存在父亲(包括根)的和也为s)遇到距离为s的点的个数,当前的点为q,距离为s,
q对答案的贡献至少为cs,如果当前点存在父亲距离也等于s,那么还要加上Cs,因为我们必须找一个点作为休息点,
每轮更新一下数组即可
复杂的:O(2∗nlog(n));
Code
#include<iostream> #include<cstdlib> #include<cstdio> #define fo(i,a,b) for(int i=a;i<=b;i++) using namespace std; typedef long long LL; const int N=100500,maxlongint=2147483640; int read(int &n) { n=0;int q=0,w=1;char ch=getchar(); for(;(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-')w=-1,ch=getchar(); for(;ch>='0'&&ch<='9';q=q*10+ch-48,ch=getchar()); n=q*w;return n; } int B[2*N][3],A ,B0; int n,hw ,c[2*N],c1[2*N]; LL ans; int alln,ce,ces,z[2*N]; bool bj ; void join(int q,int w,int e) { B[++B0][0]=A[q],A[q]=B0,B[B0][1]=w,B[B0][2]=e; B[++B0][0]=A[w],A[w]=B0,B[B0][1]=q,B[B0][2]=e; } int dfs(int q,int fa) { int w;hw[q]=1; for(int i=A[q];i;i=B[i][0])if(!bj[w=B[i][1]]&&fa!=w)hw[q]+=dfs(w,q); return hw[q]; } void findc(int q,int fa) { int s=alln-hw[q],w; for(int i=A[q];i;i=B[i][0])if(!bj[w=B[i][1]]&&fa!=w)findc(w,q),s=max(s,hw[B[i][1]]+1); if(ces>s)ces=s,ce=q; } void add(int q,int s0,int fa) { c[s0+N]+=(z[s0+N]!=0);c1[s0+N]+=(z[s0+N]==0);z[s0+N]++; for(int i=A[q];i;i=B[i][0])if(!bj[B[i][1]]&&fa!=B[i][1])add(B[i][1],s0+B[i][2],q); z[s0+N]--; } void find(int q,int s0,int fa) { ans+=c[N-s0]+(z[s0+N]!=0)*c1[N-s0]; z[s0+N]++; for(int i=A[q];i;i=B[i][0])if(!bj[B[i][1]]&&fa!=B[i][1])find(B[i][1],s0+B[i][2],q); z[s0+N]--; } void divide(int q) { alln=dfs(q,q); ces=maxlongint; findc(q,q);bj[q=ce]=1; fill(c+N-alln-1,c+N+alln+1,0);fill(c1+N-alln-1,c1+N+alln+1,0);c1 =1; for(int i=A[q];i;i=B[i][0])if(!bj[B[i][1]]) { z =0;find(B[i][1],B[i][2],q); z =1;add(B[i][1],B[i][2],q); } for(int i=A[q];i;i=B[i][0])if(!bj[B[i][1]])divide(B[i][1]); } int main() { int q,w,e; read(n); fo(i,1,n-1)read(q),read(w),read(e),join(q,w,(e?1:-1)); ans=0; divide(1); printf("%lld\n",ans); return 0; }
相关文章推荐
- Linux基本命令【一】
- hadoop 配置记录
- OpenNI(开放自然交互)是一个多语言,跨平台的框架
- Openlayers热力图层
- Kinect开发教程一:OpenNI的安装与开发环境配置
- CoreOS项目
- ROS为Robot Operating System(机器人操作系统)
- 【操作系统】CentOS 7 ftp 使用教程
- ubuntu安装openwatcom时Core Dump 错误
- linux socket的IO多路复用简单例子(二)
- Linux下JDK安装
- caffe学习笔记1:ubuntu15.04下3分钟完成caffe环境配置(基于docker)
- Linux基础命令
- Openvas使用
- Ubuntu 下环境搭建系列 —— 安装 Apache Maven
- LINK : fatal error LNK1104: 无法打开文件“opencv_calib3d249d.lib”问题解决
- sqoop导入导出
- 【Nginx学习】url地址补全最后的/(反斜杠)
- Linux 下 ascii 的查看方式
- opencv2.4.10【附加的依赖项】