[JZOJ3234] 阴阳
题目
题目大意
有一棵树,每条边有\(0\)或\(1\)两种颜色。
求满足存在\((u,v)\)路径上的点\(x\)使得\((u,x)\)和\((x,v)\)路径上的两种颜色出现次数相同的点对\((u,v)\)数量。
思考历程
在看到这题之前我就已经知道这题是点分治了……
然而看了题目后,很长一段时间搞错了题目大意……
后来终于搞懂了题目大意,于是开始想正解。
想了半天,心态崩了……点分治我在差不多一年前才打过一题啊!
很久后搜题解,粗略看一看,没有一个看得懂……
于是又开始自己刚……然后刚出来了……
结果WA了……
调了不知道多久,终于AC……
正解
正解就是点分治啦……
显然的思路是将\(0\)颜色的边权值记为\(1\),\(1\)颜色的边权值记为\(-1\)。
如果\((u,v)\)满足条件,一定有点\(x\)使得\((u,x)\)和\((x,v)\)路径上的权值和为\(0\)。
现在设重心为\(root\),现在我们要求的路径必须经过\(root\)。
求出每个点到\(root\)路径上的权值和\(dis_x\)。
显然,如果\((u,v)\)满足条件,一定有\(dis_u+dis_v=0\)。
还有一个条件是存在\(u\)或\(v\)的祖先\(x\),使得\(dis_x=dis_u\)或\(dis_x=dis_v\)。
然后我们就搬出几个桶……
设\(tot_i\)表示\(dis\)值为\(i\)的点的数量,\(can_i\)表示\(dis\)值为\(i\)并且其祖先有\(dis\)值和它相同的点的数量。这两个都是前面计算过的\(root\)的所有子树的。同理,设\(sub_i\)和\(scn_i\),意义相同,表示现在正在计算的这个子树的。
\(sub_i\)的计算方法显然,至于\(scn_i\),我们在\(dfs\)的过程中再维护一个桶\(anc_i\),表示\(dis\)值为\(i\)的祖先有多少个,这样就可以快速判断了。
\(tot_i\)和\(can_i\)可以由后两者累加而得。
计算答案的时候,枚举\(dis\)值,用前面四个桶里的值来计算,具体来说,如果\((u,v)\)能被算入答案中,则\(u\)和\(v\)至少有一个在\(can\)或\(scn\)中。
注意,我们还没有计算\(v=root\)的情况。针对这种情况,我们维护一个\(cnt0\),在\(dfs\)的时候遇到\(anc_{dis_i}>1\)时,便意味着除了\(root\)之外,还有一个和它\(dis\)值相同的祖先,那就将其加入\(cnt0\)中。答案加上\(cnt0\)即可。
所以说实际上这是一道点分治的模板题啊……
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cassert>
#define N 100010
int n;
struct EDGE{
int to,len;
EDGE *las;
bool ok;
} e[N*2];
int ne;
EDGE *last[N];
inline void link(int u,int v,int len){
e[ne]={v,len,last[u],1};
last[u]=e+ne++;
}
#define rev(ei) (e+(int((ei)-e)^1))
long long ans;
int siz[N],all;
void get_siz(int x,int fa){
siz[x]=1;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa && ei->ok){
get_siz(ei->to,x);
siz[x]+=siz[ei->to];
}
}
int find(int x,int fa){//找重心
bool bz=(all-siz[x]<<1<=all);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa && ei->ok){
int tmp=find(ei->to,x);
if (tmp)
return tmp;
bz&=(siz[ei->to]<<1<=all);
}
if (bz)
return x;
}
int dis[N];
int _tot[N*2],*tot=_tot+N;//为了代码方便,所以用指针来处理了……
int _sub[N*2],*sub=_sub+N;
int _can[N*2],*can=_can+N;
int _scn[N*2],*scn=_scn+N;
int cnt0;
int _anc[N*2],*anc=_anc+N;
int mn,mx,smn,smx;//记录mn,mx是为了方便清空桶。
void get_dis(int x,int fa){
smn=min(smn,dis[x]),smx=max(smx,dis[x]);
sub[dis[x]]++;
if (anc[dis[x]]){
scn[dis[x]]++;
if (anc[dis[x]]>1 && dis[x]==0)
cnt0++;
}
anc[dis[x]]++;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa && ei->ok){
dis[ei->to]=dis[x]+ei->len;
get_dis(ei->to,x);
}
anc[dis[x]]--;
}
void divide(int x){
get_siz(x,0);
all=siz[x];
int root=find(x,0);
dis[root]=0;
mn=INT_MAX,mx=INT_MIN;
anc[0]=1;
for (EDGE *ei=last[root];ei;ei=ei->las)
if (ei->ok){
smn=INT_MAX,smx=INT_MIN;
cnt0=0;
dis[ei->to]=dis[root]+ei->len;
get_dis(ei->to,root);
for (int j=smn;j<=smx;++j)
ans+=1ll*sub[j]*can[-j]+1ll*scn[j]*tot[-j]-1ll*scn[j]*can[-j];//容斥原理
ans+=cnt0;
for (int j=smn;j<=smx;++j){
tot[j]+=sub[j],sub[j]=0;
can[j]+=scn[j],scn[j]=0;
}
mn=min(mn,smn);
mx=max(mx,smx);
}
anc[0]=0;
for (int i=mn;i<=mx;++i)
tot[i]=can[i]=0;
for (EDGE *ei=last[root];ei;ei=ei->las)
if (ei->ok){
ei->ok=rev(ei)->ok=0;
divide(ei->to);
}
}
int main(){
scanf("%d",&n);
for (int i=1;i<n;++i){
int u,v,type;
scanf("%d%d%d",&u,&v,&type);
link(u,v,type==0?1:-1);
link(v,u,type==0?1:-1);
}
divide(1);
printf("%lld\n",ans);
return 0;
}
总结
点分治的题目还是打得太少了……
[JZOJ3234] 阴阳的更多相关文章
- 伏羲八卦、文王六十四卦、老子阴阳太极、西方哲学辩证与"解耦和复用”思想的异曲同工之妙
伏羲八卦.文王六十四卦.老子阴阳太极.西方哲学辩证与"解耦和复用”思想的异曲同工之妙 问题:任何程序语言在遇到复杂逻辑时,代码维护难度就会加大,如何处理该问题? 答案:重构,模块化. ...
- scheme 阴阳谜题
本篇分析continuation的一个著名例子"阴阳迷题",这是由David Madore先生提出的,原谜题如下: (let* ((yin ((lambda (foo) (disp ...
- 使用CSS达到阴阳八卦图等图形
CSS还是比較强大的,能够实现中国古典的"阴阳八卦图"等形状. 正方形 #rectangle { width: 200px; height: 100px; backgrount-c ...
- CSS3_边框 border 详解_一个 div 的阴阳图
(面试题) 怎么样通过 CSS 画一个三角形: 1. 元素的 width 和 height 设置为 0 2. 边框 足够大 3. 需要的三角形的部分, border-top-color 设置为 ...
- Python turtle绘制阴阳太极图代码解析
本文详细分析如何使用Python turtle绘制阴阳太极图,先来分解这个图形,图片中有四种颜色,每条曲线上的箭头表示乌龟移动的方向,首先从中心画一个半圆(红线),以红线所示圆的直径作半径画一个校园, ...
- UI设计教程分享:字体变形—阴阳收缩法
阴阳师中国古代对自然规律发展变化基础因素的描述,是古代美学逻辑思维.推理分析的核心要素,也是描述万物基本要素和成因的概念之一.阴阳代表事物的对立关系,它是自然界的客观规律,是万物运动变化的本源,是人类 ...
- css基本图形绘制(基本的矩形、圆形、椭圆、三角形、多边形,也包括稍微复杂一点的爱心、钻石、阴阳八卦等)
正方形: 代码: <style> .square { width: 100px; height: 100px; background-color: cornflowerblue; } &l ...
- python之demo2----改编自python官方提供的turtle_yinyang.py画阴阳的demo
""" 执行 python -m turtledemo 命令查看系统内置demo的源码 绘制:需要通过import turtle引入绘制图形库turtle库 改编自pyt ...
- Rust 阴阳谜题,及纯基于代码的分析与化简
Rust 阴阳谜题,及纯基于代码的分析与化简 雾雨魔法店专栏 https://zhuanlan.zhihu.com/marisa 来源 https://zhuanlan.zhihu.com/p/522 ...
随机推荐
- 使ie8正常支持placeholder
在IE8下测试,发现一个问题placeholder不被支持,下面是解决IE支持placeholder的方法,本文引用的jquery是1.12.0测试通过,先引用jquery <script ty ...
- 【牛客提高训练营2B】分糖果
题目 发现自己一年之前非常垃圾 题目大意是给你一个\(n\)个点的环,给每个点一个\([1,a_i]\)的取值,并且满足环上任意相连两点权值不能相等,求方案数 考虑断环为链,发现不大会 不妨考虑所有\ ...
- win7+ubuntu双系统安装
XP+ubuntu双系统可參考 xp硬盘上安装ubuntu12.04双系统 1.XP安全不再,最终将吾等磨机的人逼上梁山,是时候做一个win7+ubuntu的双系统了. 废话少说,直接进入主题,先腾出 ...
- JS设置浏览器缓存,以及常用函数整理
//设置缓存 function set_cache(key,value){ if(key=='') return false; localStorage.setItem(key, value); } ...
- 微信小程序开发系列之Hello World
第一步:注册 在微信公众平台官网首页,点击注册.(相关文档可以找到,这里不再累述,望见谅.) 微信小程序注册成功后界面 第二步:编辑器.开发工具 我们假定你已经申请注册好微信小程序了,我们选定一个代码 ...
- u-boot 移植工作目录
1. 添加工作用户 [root@localhost ~]#useradd -G root -g root -d/home/uboot uboot 2. 建立工作目录 [uboot@localhost ...
- 关于hadoop多次format之后,会出现的dataNode消失问题
如标题,最近我由于想初始化以下hadoop集群,之后却发现启动集群后所有的DataNode都消失了. 问题查找: 由于时所有的DataNode都出了问题,于是我翻找了以下DataNode的日志(默认在 ...
- egrep 或与
' HEADERABC' .txt | .txt 'HEADERABC' .txt > .txt .txt > .txt ' HEADERABC' ' TAILER123' .txt &g ...
- Android开发 MediaPlayer入门_播放本地视频
前言 MediaPlayer,可以播放视频/音频,并且它支持本地和网络文件的播放.本片博客作为入门教程,先以最通俗的方式解释播放文件本地视频.(如果你嫌MediaPlayer还是太麻烦可以试试选择Vi ...
- Q:简单实现URL只能页面跳转,禁止直接访问
sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据,且不同标签页的session不能共享,通过此特性来控制某个页面只能通过上级页面同标签页跳转 ...