CF1930G Prefix Max Set Counting 题解
题意:
给定一棵以 1 为根的有根树,求出其所有 dfs 序中前缀最大值序列的数量。\(n\le 10^6\)。
思路
显然考虑 DP。
由于是求前缀最大值序列的方案数,因此如果一些点要出现在这个序列中,其到根节点上一定没有比它大的节点。
因此我们设 \(f_i\) 表示以权值为 \(i\) 的点为结尾的序列数量。可以发现权值最大的一定在这个序列末尾,权值最小的根节点 1 一定在这个序列的开头,因此这个 DP 的初始状态是 \(f_1=1\),答案就是 \(f_n\)。
我们定义某个点对另一些点“有贡献” 指这另一些点的方案数可以从这个点转移过来。具体而言,这个转移就是使当前点作为序列倒数第二个点,被贡献的点作为第一个点,去加被贡献的点的方案数。
发现如果按树点的权值顺序从小到大枚举,可以通过刷表法来维护某一个点。因为只有小的点可能对大的点有贡献,而大的点不可能对小的点有贡献,因此这样统计是完备的。
按这个思路,我们来分类讨论就可以计算贡献了。假设当前点为 \(i\),其权值同样也为 \(i\)。(注意我们是按权值大小的顺序枚举的)
以 \(i\) 为根的子树内没有比它大的点。
那就考虑它什么时候可以作为序列的倒数第二个点。我们向上枚举 \(i\) 的祖先 \(u\),假设 \(u\) 及其子树中权值最大的点是 \(j\),那么当 \(j>i\) 的时候,\(u\) 及其子树中的点就有了一种走的方式:
先走到 \(i\),再走到 \(j\) 就保证以 \(j\) 自己结尾的方案数必定可以累加上 \(i\) 的方案数。因此我们直接给 \(u\) 子树中权值比 \(i\) 大的方案数加上 \(f_i\) 即可。
注意这里找 \(u\) 可以直接暴力跳父亲,因为有均摊,找 \(u\) 的总复杂度 \(O(n)\)。以 \(i\) 为根的子树内有比其大的点。
因为 dfs 序是一定要一棵子树搜完以后再去其他子树,因此对于除了以 \(i\) 为根的其他子树而言 \(i\) 作为序列的倒数第二位已经不可能了。
因此直接将 \(i\) 自己子树中权值大于 \(i\) 的节点的方案数加上 \(f_i\) 即可。
然后发现由于权值比 \(i\) 小的所有数全部都已经枚举完了,加不加无所谓,因此加的时候不需要管权值是否大于 \(i\),直接把整棵子树全部都加即可。
于是差分后的树状数组即可满足条件。在 dfs 序上维护,区间加单点查。
code
事实上感觉没有黑题的难度。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+7,p=998244353;
int n,f[N],fa[N],siz[N],dfncnt,dfn[N],mson[N],sign[N],tot[N];
vector<int> q[N];
struct node{
int tr[N];
void modify(int x,int val){while(x<=n)tr[x]=(tr[x]+val)%p,x+=x&(-x);}
int query(int x){int res=0;while(x)res=(res+tr[x])%p,x-=x&(-x);return res;}
}g;
void dfs1(int u,int fr,int t){
siz[u]=1;fa[u]=fr;dfn[u]=++dfncnt;mson[u]=0;sign[u]=t<u?1:0;
for(int i=0;i<tot[u];i++){
int v=q[u][i];if(v==fr) continue;
dfs1(v,u,max(t,u));siz[u]+=siz[v];mson[u]=max({mson[u],v,mson[v]});
}
}
void solve(){
for(int i=0;i<=n;i++) q[i].clear(),tot[i]=0,f[i]=0,sign[i]=0,g.tr[i]=0;dfncnt=0;
cin>>n;for(int i=1,u,v;i<=n-1;i++){cin>>u>>v;q[u].push_back(v),tot[u]++;q[v].push_back(u),tot[v]++;}
dfs1(1,0,0);f[1]=1;g.modify(1,1);
for(int i=2;i<=n;i++){
if(!sign[i]) continue;
f[i]=g.query(dfn[i]);g.modify(dfn[i]+siz[i],f[i]),g.modify(dfn[i],p-f[i]);
if(i>mson[i]){
int u=i;
while((max(u,mson[u])<=i)&&u) {u=fa[u];}
if(u==0) continue;
g.modify(dfn[u],f[i]);g.modify(dfn[u]+siz[u],p-f[i]);
}
else g.modify(dfn[i],f[i]),g.modify(dfn[i]+siz[i],p-f[i]);
}
cout<<f[n]<<'\n';
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--) solve();
return 0;
}
CF1930G Prefix Max Set Counting 题解的更多相关文章
- CSAcademy Prefix Suffix Counting 题解
CSAcademy Prefix Suffix Counting 题解 目录 CSAcademy Prefix Suffix Counting 题解 题意 思路 做法 程序 题意 给你两个数字\(N\ ...
- 【luogu P3128 [USACO15DEC]最大流Max Flow】 题解
题目链接:https://www.luogu.org/problemnew/show/P3128 菜 #include <cstdio> #include <cstring> ...
- [USACO17JAN]Promotion Counting 题解
前言 巨佬说:要有线段树,结果蒟蒻打了一棵树状数组... 想想啊,奶牛都开公司当老板了,我还在这里码代码,太失败了. 话说奶牛开个公司老板不应该是FarmerJohn吗? 题解 刚看到这道题的时候竟然 ...
- POJ 2386 Lake Counting 题解《挑战程序设计竞赛》
地址 http://poj.org/problem?id=2386 <挑战程序设计竞赛>习题 题目描述Description Due to recent rains, water has ...
- CF1106A Lunar New Year and Cross Counting 题解
Content 试求出在一个 \(n\times n\) 的地图 \(M\) 中,满足 \(1\leqslant i,j\leqslant n\) 且 \(M_{i,j}=M_{i+1,j+1}=M_ ...
- 【LeetCode】14. Longest Common Prefix 最长公共前缀
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:prefix, 公共前缀,题解,leetcode, 力扣 ...
- 山东省第四届ACM大学生程序设计竞赛解题报告(部分)
2013年"浪潮杯"山东省第四届ACM大学生程序设计竞赛排名:http://acm.upc.edu.cn/ranklist/ 一.第J题坑爹大水题,模拟一下就行了 J:Contes ...
- SP1716 GSS3 - Can you answer these queries III
题面 题解 相信大家写过的传统做法像这样:(这段代码蒯自Karry5307的题解) struct SegmentTree{ ll l,r,prefix,suffix,sum,maxn; }; //.. ...
- [国家集训队2012]middle(陈立杰)
我是萌萌的传送门 我是另一个萌萌的传送门 脑残错误毁一下午…… 其实题解早就烂大街了,然而很久之前我只知道是二分答案+主席树却想不出来这俩玩意儿怎么一块儿用的……今天又翻了几篇题解才恍然大悟,是把权值 ...
- Leetcode: Maximum XOR of Two Numbers in an Array
Given a non-empty array of numbers, a0, a1, a2, - , an-1, where 0 ≤ ai < 231. Find the maximum re ...
随机推荐
- Esp32s3(立创实战派)移植LVGL
Esp32s3(立创实战派)移植LVGL 移植: 使用软件EEZ studio 创建工程选择带EEZ Flow的,可以使用该软件更便捷的功能 根据屏幕像素调整画布为320*240 复制ui文件至工程 ...
- 爱快路由-宽带刷下行流量docker版教程
疑难解答加微信机器人,给它发:进群,会拉你进入八米交流群 机器人微信号:bamibot 简洁版教程访问:https://bbs.8miyun.cn 一天天看直播上行流量太高,给你下行加加速吧,让你下行 ...
- Linux - top相关的快捷键
q:退出top命令窗口(quit). k:按照进程ID终止(kill)一个进程.例如,你可以输入k,然后输入进程的PID来终止它. r:重新设置进程的优先级.输入r后,你可以输入新的优先级值. f:进 ...
- [解决方案]git pull : error: cannot lock ref 'refs/remotes/origin/*' (unable to update local ref)
错误 git pull 报错不能更新本地分支 错误分析 本地分支跟远程分支不匹配 导致更新失败 解决方案 备份自己修改的代码 .git\refs\remotes (文件路径)对应删除你报错的分支 gi ...
- 通过 C# 打印Word文档
Word文档是日常办公和学习中不可或缺的一部分.比如在商务往来中,经常需要打印 Word 文档用于撰写和传递正式的商务信函.合作协议.项目提案等.打印出来的文档便于双方签字盖章,具有法律效力和正式性. ...
- springboot报错说 Failed to parse multipart servlet request; nested exception is java.io.IOException
问题:一次开发中遇到一个springboot的异常,如下所示:Failed to parse multipart servlet request; nested exception is java.i ...
- js解析json字符串、对象与json之间的转换
前言 在数据传输流程中,json是以文本,即字符串的形式传递的,而JS操作的是JSON对象,所以,JSON对象和JSON字符串之间的相互转换是关键. js解析json字符串 // JSON字符串 'v ...
- 再谈MCP协议,看看 MCP 是如何重塑 AI 与外部数据源互动的能力?
Techscribe Central 缩略图由 Techscribe Central 制作和编辑 MCP!!是不是一头雾水?我当时也是这个反应.我也是最近才听说它开始引发关注,然后我发现大多数人根本不 ...
- vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果
2025 AI实战vue3+deepseek+arcoDesign仿DeepSeek/豆包网页版AI聊天助手. vue3-web-deepseek 实战网页PC版智能AI对话,基于vite6+vue3 ...
- Hive SQL实现近N周的数据统计查询
文/朱季谦 先前遇到过一个需求,需要基于HIVE统计近N周范围的数据,例如,统计近7周范围的数据指标. 需要用HIVE SQL去实现该功能,而HIVE SQL并没有PostgreSQL那样例如通过函数 ...