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

题目: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;
}

原文地址:https://www.cnblogs.com/yu-xing/p/11252744.html

时间: 2024-10-09 19:54:07

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

深度 | 邢波教授谈人工智能科学路径:为人工智能装上「无穷动」引擎

深度 | 邢波教授谈人工智能科学路径:为人工智能装上「无穷动」引擎 本文由邢波教授 授权转载 微信公众号:猫匣子 作者:邢波 有些人是因为对人工智能的原理不理解而导致,有些人是为了个人名望而宣扬人工智能威胁论,有些人则是为了商业的利益推动人工智能威胁论.——Yann LeCun   人工智能算法本身这个计算任务像登山,它是有明确的目标的,可以用数学描述和比较的目标,它本身有弹性.容错性.随机性.和客观的量化的可评估性 … 人工智能功能的提升和突破来源于理论模型,算法,和计算引擎的不断创新,缺一不

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主 试题描述 "A fight? Count me in!" 要打架了,算我一个. "Everyone, get in here!" 所有人,都过来! 小Y是一个喜欢玩游戏的OIer.一天,她正在玩一款游戏,要打一个Boss. 虽然这个Boss有 \(10^{100}\) 点生命值,但它只带了一个随从--一个只有 \(m\) 点生命值的"恐怖的奴隶主". 这个"恐怖的奴隶主&qu

[LOJ#2328]「清华集训 2017」避难所

[LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所了." "唔,那你之后会去哪呢?" "去一个没有冬天的地方." 对于一个正整数 \(n\),我们定义他在 \(b\) 进制下,各个位上的数的乘积为 \(p = F(n, b)\). 比如 \(F(3338, 10) = 216\). 考虑这样一个问题,已知 \

[LOJ#2327]「清华集训 2017」福若格斯

[LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最左边的两个格子上各有一个向右的青蛙,最右边的两个格子上各有一个向左的青蛙. 每次移动可以选取一个青蛙,向这只青蛙的前方移动一格到空格子中或跳过前方的一个不同朝向的青蛙并移动到空格子中. 为了使你更好地理解这个游戏,我们下发了一个游戏demo作为参考(注意:这个demo中的棋盘大小和题目中并不相同).

[LOJ#2331]「清华集训 2017」某位歌姬的故事

[LOJ#2331]「清华集训 2017」某位歌姬的故事 试题描述 IA是一名会唱歌的女孩子. IOI2018就要来了,IA决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符,第iii个音符的音高为 \(h_i\).IA的音域是 \(A\),她只能唱出 \(1\sim A\) 中的正整数音高.因此 \(1\le h_i\le A\). 在写歌之前,IA需要确定下这首歌的结构,于是她写下了 \(Q\) 条限制,其中第 \(i\) 条为:编号在 \(l_i\) 到 \(r_

「清华集训2014」主旋律

「清华集训2014」主旋律 这个题好难难啊,我想了一个小时连50分都不会,只能去摸周指导了. 我们试图直接爆算集合 \(S\) 的非强连通导出子图数量,考虑将这个导出子图的所有强连通分量缩点后,一定是一个点数 \(\geq 2\) 的 \(\text{DAG}\) .即缩完以后至少要有一个入度为 \(0\) 的点,这个条件充分性显然,必要性考虑如果不存在入度为 \(0\) 的点,那么一定存在一个环,说明并没有将所有强连通分量缩掉.然后我们枚举入度为 \(0\) 的点集 \(T\) ,记 \(F(

「清华集训2014」矩阵变换

「清华集训2014」矩阵变换 解题思路 问题转化为找一个行与选的数字的完美匹配,记 \(pos[i][j]\) 为数字 \(i\) 在第 \(j\) 行的出现位置,要求不存在匹配边 \((a,b),(c,d)\) 使得 \(pos[b][a] < pos[b][c]\ \& \ pos[d][c] > pos[b][c]\) . 观察发现,这个式子相当于让一条边的权值看做 \(pos[x][y]\) ,当存在一个行与一个数没有匹配但是强行让它们匹配后数的权值会变大,行的权值会变小,这个

「下载神器」aria2 懒人安装教程 [Windows]

是一款开源.轻量级的多协议命令行下载工具,支持 HTTP/HTTPS.FTP.SFTP.BitTorrent 和 Metalink 协议,拥有众多第三方支持插件,被誉为「下一代下载工具」和「下载神器」,然而由于安装配置复杂,挡住了许多人的使用.青小蛙带来 aria2 懒人版安装教程,简单粗暴,下面就开始.@Appinn 由于是懒人版安装教程,我们先不深究如何具体配置 aria2 了,直接拿来用就好了. 首先,下载青小蛙重新打包了一下的懒人版 aria2 程序包(百度盘链接,密码: gafp):

微信小程序开发基础(一)「配置」与「逻辑层」

微信小程序作为微信生态重要的一环,在实际生活.工作.商业中的应用越来越广泛.想学习微信小程序开发的朋友也越来越多,本文将在小程序框架的基础上就微信小程序项目开发所必需的基础知识及语法特点进行了详细总结和阐述,包括配置.函数.语法.事件及其处理.数据绑定.模块.样式等.想开发小程序,这些内容是必须掌握的. 全文知识结构预览: 一.程序配置: 1.全局配置:2.页面配置 二.逻辑层: 1.程序注册:App()方法:2.页面注册:Page()方法:3.模块与调用:4.微信原生API 三.视图层(将在单