题目:P4930「FJ2014集训」采药人的路径

思路:

这篇不算题解,是让自己复习的,什么都没说清楚。

很久没有写点分治了,以前为了赶课件学的太急,板子都没打对就照着题解写题,导致学得很不扎实。

这道题差不多是在郭老师的指导下一点点凑出来的,还是没能自己完整写出一道题,惭愧。

这道题大意是:给出一棵边权为0/1的树,求满足以下条件的路径总数:0的个数等于1的个数,且路径上存在一点到路径两端也满足该条件。

这种求路径总数的题,可以想到用点分治。

把0看作-1,就可以转化为路径边权和为0。

如果没有休息点这个条件是很容易统计的,加上这个条件我们就要多记一些信息了。

点分治的容斥写法应用范围有限,用另一种写法会更方便。

类似树形背包,我们在统计经过u点的路径条数时,维护了一个之前遍历过的子树上的路径集合,每次进入一棵新子树,记录一下现在走的链,和之前集合里的链拼在一起形成答案,最后把现在的链加入集合中。

所以对这道题,我们维护现在走的路径和之前的路径集合时都分作两类:有休息点和没有休息点。

现在有休息点的路径某段后缀和为0,有一段和为0等价于有两点前缀和相等,开桶记录前缀和,类似P3941 入阵曲那道题。

没有休息点的路径用路径集合中有休息点的路径匹配,有休息点的路径之前有没有休息点都能匹配。

现在走的有休息点的路径可能本身已经满足条件了,要记入答案。

注意:休息点也可以不在路径上某点,而在根节点,即两边各有一条“1、-1”的路径,要加上这种情况。


Code:

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+5,inf=0x3f3f3f3f;
int n,rt,rtsiz,tot[2],d[2][N],siz[N];
long long ans;
bool del[N];
struct bucket{//桶+时间戳
int tim,vis[N],c[N];
inline void add(int pos,int val=1){
if(vis[n+pos]==tim) c[n+pos]+=val;
else vis[n+pos]=tim,c[n+pos]=val;
}
inline int ask(int pos){
if(vis[n+pos]==tim) return c[n+pos];
else return 0;
}
}cnt[2],vis;
int Top,ver[N],val[N],nxt[N],head[N];
inline void add(int x,int y,int z){
ver[++Top]=y;val[Top]=z;nxt[Top]=head[x];head[x]=Top;
}
void findrt(int u,int fa,int all){//找重心
siz[u]=1;
int mxsiz=0;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa||del[v]) continue;
findrt(v,u,all);
siz[u]+=siz[v];
mxsiz=max(mxsiz,siz[v]);
}
mxsiz=max(mxsiz,all-siz[u]);
if(mxsiz<rtsiz) rtsiz=mxsiz,rt=u;
}
void getdis(int u,int fa,int dis){
int op=1;
if(vis.ask(dis)==0) vis.add(dis),op=0;//之前没有出现相同路径长度 不存在休息点
d[op][++tot[op]]=dis;//记录路径长
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa||del[v]) continue;
getdis(v,u,dis+val[i]);
}
if(!op) vis.add(dis,-1);//如果是第一次出现 就退栈 倒出桶
}
void calc(int u){
++cnt[0].tim;++cnt[1].tim;++vis.tim;//更新时间戳 相当于memset
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(del[v]) continue;
tot[0]=tot[1]=0;
getdis(v,u,val[i]);
for(int j=1;j<=tot[0];++j) ans+=cnt[1].ask(-d[0][j]);//没有休息点的路径 用以前有休息点的路径匹配
for(int j=1;j<=tot[1];++j) ans+=cnt[0].ask(-d[1][j])+cnt[1].ask(-d[1][j]);//有休息点的路径 之前有没有休息点都能匹配
for(int j=1;j<=tot[0];++j) if(d[0][j]==0) ans+=cnt[0].ask(0);//特判
for(int j=1;j<=tot[0];++j) cnt[0].add(d[0][j]);//把当前路径加入路径集合
for(int j=1;j<=tot[1];++j) {
if(d[1][j]==0) ++ans;//注意当前路径也可能贡献答案
cnt[1].add(d[1][j]);
}
}
}
void dfs(int u){
del[u]=true;//删掉u点
calc(u);//计算过u点路径条数
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(del[v]) continue;
rtsiz=inf;
findrt(v,u,siz[v]);
dfs(rt);
}
}
int main(){
scanf("%d",&n);
for(int i=1,u,v,w;i<=n-1;++i){
scanf("%d%d%d",&u,&v,&w);
if(!w) w=-1;
add(u,v,w);
add(v,u,w);
}
rtsiz=inf;
findrt(1,0,n);
dfs(rt);
printf("%lld\n",ans);
return 0;
}

P4930「FJ2014集训」采药人的路径的更多相关文章

  1. 「FJ2014集训」采药人的路径

    啦啦啦 来写一篇题解 洛谷链接: P4930 「FJ2014集训」采药人的路径 统计路径?嗯往点分治上想. 把0和1转化为-1和1,求和完dis为0的路径就是阴阳平衡的路径了. 如果题目没有限制要有中 ...

  2. 「BZOJ3694」「FJ2014集训」最短路

    「BZOJ3694」「FJ2014集训」最短路 首先树剖没得说了,这里说一下并查集的做法, 对于一条非树边,它会影响的点就只有u(i),v(i)到lca,对于lca-v的路径上所有点x,都可通过1-t ...

  3. 「FJ2014集训 采药人的路径」

    题目 考虑一下把\(0\)看成\(-1\),那么就是找到一条边权和为\(0\)的路径,且这条路径可以被分成两段,边权和都是\(0\) 没有第二个限制就是点分裸题了 其实有了第二个限制还是点分裸题 考虑 ...

  4. bzoj3697_FJ2014集训_采药人的路径_solution

    小道士的矫情之路: 点分治, 对于每个子树,处理其内经过根(重心)的路径,然后递归下一层子树: 如何处理经过根的合法路径 合法有两个要求: 把输入的0改成-1后 1.len=0; 2.存在一个点i使被 ...

  5. [LOJ#2330]「清华集训 2017」榕树之心

    [LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...

  6. Loj #2331. 「清华集训 2017」某位歌姬的故事

    Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...

  7. Loj #2324. 「清华集训 2017」小 Y 和二叉树

    Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...

  8. Loj #2321. 「清华集训 2017」无限之环

    Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...

  9. Loj 2320.「清华集训 2017」生成树计数

    Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...

随机推荐

  1. Web应用托管服务(Web+)隐藏的十个上云最佳姿势

    随着云计算浪潮的推进,技术架构云化已经成为大势所趋.特别是最近由CNCF推动的云原生概念,将符合云原生标准的各种开源技术方案推向了前所未有的高度.在这一波浪潮的推动下,越来越多的企业开始了自身的数字化 ...

  2. Ubuntu18.04 + win10双系统下时间问题

    Ubuntu 16.04使用systemd启动之后,时间也改成了由timedatectl来管理 解决的办法可以参考http://blog.sina.com.cn/s/blog_5379c55b0102 ...

  3. loj2322 「清华集训 2017」Hello world!

    https://loj.ac/problem/2322 先吐槽一下,sb数据毁我青春败我前程. 首先,一个数开根开不了多少次. 当我们把它开到1的时候,我们以后就不需要开他了,我们可以利用并查集跳过他 ...

  4. PHP实现微信小程序支付完整版,可以借鉴!

    本文实例为大家分享了php实现小程序支付的具体代码,供大家参考,具体内容如下 环境: tp3.2.3  + 小程序 微信支付功能开通 Step1:  下载PHP 支付SDK(下载地址)  放到Libr ...

  5. PHP实现git部署的方法,可以学学!

    https://mp.weixin.qq.com/s/QFpKu8oKoxOEA1BmT7pNhg   在小站点上,直接用git来部署php代码相当方便,你的远程站点以及本地版本库都有一个版本控制,追 ...

  6. spring cloud深入学习(三)-----服务消费

    在上一篇博文中简单实现了eureka-server以及eureka-provider,后面会实现eureka-cosumer,现在针对eureka做进一步的详解. 微服务整体架构 文字再美也没有图片直 ...

  7. eclipse配置mybatis xml文件自动提示

    如果使用eclipse中,再写mybatis的xml文件的时候,没有提示,用“Alt+/”,不能把代码用快捷键敲出来,通过下面这个方法,可以解决. 1.下载一个文件,找一个专门的地方保存,配置自动提示 ...

  8. MyBatis中动态加载where条件

    1.trim 2.where 1 = 1 where 1=1 <if test="beginDate !=null and beginDate !='' "> and ...

  9. 微信小程序-- WXML语法

    页面数据 test.js test.wxml 运行结果:

  10. 第十章—DOM(一)——Document类型

    DOCUMENT类型 JS通过document类型表示文档,在文档中document对象是HTMLDocument的一个实例,表示整个HTML页面.document对象是window对象的一个属性,因 ...