题意:

给定一棵以 1 为根的有根树,求出其所有 dfs 序中前缀最大值序列的数量。\(n\le 10^6\)。

思路

显然考虑 DP。

由于是求前缀最大值序列的方案数,因此如果一些点要出现在这个序列中,其到根节点上一定没有比它大的节点。

因此我们设 \(f_i\) 表示以权值为 \(i\) 的点为结尾的序列数量。可以发现权值最大的一定在这个序列末尾,权值最小的根节点 1 一定在这个序列的开头,因此这个 DP 的初始状态是 \(f_1=1\),答案就是 \(f_n\)。

我们定义某个点对另一些点“有贡献” 指这另一些点的方案数可以从这个点转移过来。具体而言,这个转移就是使当前点作为序列倒数第二个点,被贡献的点作为第一个点,去加被贡献的点的方案数。

发现如果按树点的权值顺序从小到大枚举,可以通过刷表法来维护某一个点。因为只有小的点可能对大的点有贡献,而大的点不可能对小的点有贡献,因此这样统计是完备的。

按这个思路,我们来分类讨论就可以计算贡献了。假设当前点为 \(i\),其权值同样也为 \(i\)。(注意我们是按权值大小的顺序枚举的)

  1. 以 \(i\) 为根的子树内没有比它大的点。

    那就考虑它什么时候可以作为序列的倒数第二个点。我们向上枚举 \(i\) 的祖先 \(u\),假设 \(u\) 及其子树中权值最大的点是 \(j\),那么当 \(j>i\) 的时候,\(u\) 及其子树中的点就有了一种走的方式:

    先走到 \(i\),再走到 \(j\) 就保证以 \(j\) 自己结尾的方案数必定可以累加上 \(i\) 的方案数。因此我们直接给 \(u\) 子树中权值比 \(i\) 大的方案数加上 \(f_i\) 即可。

    注意这里找 \(u\) 可以直接暴力跳父亲,因为有均摊,找 \(u\) 的总复杂度 \(O(n)\)。

  2. 以 \(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 题解的更多相关文章

  1. CSAcademy Prefix Suffix Counting 题解

    CSAcademy Prefix Suffix Counting 题解 目录 CSAcademy Prefix Suffix Counting 题解 题意 思路 做法 程序 题意 给你两个数字\(N\ ...

  2. 【luogu P3128 [USACO15DEC]最大流Max Flow】 题解

    题目链接:https://www.luogu.org/problemnew/show/P3128 菜 #include <cstdio> #include <cstring> ...

  3. [USACO17JAN]Promotion Counting 题解

    前言 巨佬说:要有线段树,结果蒟蒻打了一棵树状数组... 想想啊,奶牛都开公司当老板了,我还在这里码代码,太失败了. 话说奶牛开个公司老板不应该是FarmerJohn吗? 题解 刚看到这道题的时候竟然 ...

  4. POJ 2386 Lake Counting 题解《挑战程序设计竞赛》

    地址 http://poj.org/problem?id=2386 <挑战程序设计竞赛>习题 题目描述Description Due to recent rains, water has ...

  5. 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_ ...

  6. 【LeetCode】14. Longest Common Prefix 最长公共前缀

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:prefix, 公共前缀,题解,leetcode, 力扣 ...

  7. 山东省第四届ACM大学生程序设计竞赛解题报告(部分)

    2013年"浪潮杯"山东省第四届ACM大学生程序设计竞赛排名:http://acm.upc.edu.cn/ranklist/ 一.第J题坑爹大水题,模拟一下就行了 J:Contes ...

  8. SP1716 GSS3 - Can you answer these queries III

    题面 题解 相信大家写过的传统做法像这样:(这段代码蒯自Karry5307的题解) struct SegmentTree{ ll l,r,prefix,suffix,sum,maxn; }; //.. ...

  9. [国家集训队2012]middle(陈立杰)

    我是萌萌的传送门 我是另一个萌萌的传送门 脑残错误毁一下午…… 其实题解早就烂大街了,然而很久之前我只知道是二分答案+主席树却想不出来这俩玩意儿怎么一块儿用的……今天又翻了几篇题解才恍然大悟,是把权值 ...

  10. 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 ...

随机推荐

  1. 前端视角看 HTTPS

    最近用Docusaurus搭了一个个人网站,部署后看到浏览器地址栏上"不安全"三个字感觉特别辣眼,便不由自主的想起了HTTPS.回忆起自己在日常开发中遇到的一些与HTTPS相关的知 ...

  2. 13. Docker-compose容器编排(详解说明)

    13. Docker-compose容器编排(详解说明) @ 目录 13. Docker-compose容器编排(详解说明) 1. Docker-compose容器编排概述 2. Docker-Com ...

  3. 【攻防世界】wife_wife

    wife_wife 题目来源 攻防世界 NO.GFSJ1192 题解 本题没有源码,也没有提示,非常困难,在网上搜索此题可以看到源码.由于使用了assign(),因此存在Javascript原型链污染 ...

  4. Elasticsearch7.8搭建(Windows版本单节点、Linux版本单节点、集群,)

    The Elastic Stack, 包括 Elasticsearch.Kibana.Beats 和 Logstash(也称为 ELK Stack).能够安全可靠地获取任何来源.任何格式的数据,然后实 ...

  5. 《HelloGitHub》第 107 期

    兴趣是最好的老师,HelloGitHub 让你对开源感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. github.com/521xueweihan/HelloG ...

  6. 「六」Goaccess实现可视化

    下载 apt install goaccess 使用goaccess进行监控 LANG="en_US.UTF-8" bash -c 'goaccess logs/access.lo ...

  7. 运算符“<”不能应用于类型“boolean”和“RegExp”。ts-plugin(2365)

    在使用vue3 + setup + ts + volar插件开发时,遇到文件全红报这个错,尝试很多方法没有效果,最后把vscode插件全部卸载,然后重新安装就好了,如果有遇到此问题的可以尝试同样的方法 ...

  8. laradock 安装扩展程序 pcntl

    起因 运行workman脚步的时候,PHP 提示缺少 pcntl 扩展 Config git:(master) php start.php -d Please install pcntl extens ...

  9. 堆排序(topk 问题)(NB)

    博客地址:https://www.cnblogs.com/zylyehuo/ # _*_coding:utf-8_*_ # 比较排序 import random def sift(li, low, h ...

  10. Redis 原理 - Hash

    Hash 数据结构 使用 ziplist 当同时满足下面两个条件时,使用 ziplist 存储数据 元素个数少于512个 (hash-max-ziplist-entries: 512) 每个元素长度小 ...