[gym103860D]Tree Partition
D - Tree Partition
考虑将树转换到一个序列上,钦定\(1\)为根节点,\(1\)的父亲为\(0\),在序列上,孩子向父亲连边
然后考虑设\(dp\)状态\(dp[i][j]\)表示前\(i\)个点,分成\(j\)段的方案数,那么\(dp[i][j]\)从\(dp[k][j-1]\)转移过来要满足以下条件之一:
点\(i\)的后向边\((a,b)\)满足\(a\leq i\),\(b>i\),区间\([i,j]\)的前向边\((a,b)\)满足\(a\in[i,j]\),\(b<i\)
设\(x_1\)表示倒数第二个后向边的起点,\(x_2\)表示倒数第一个后向边的起点
那么对于\(<x_1\)的\(k\)不能转
\([x_1,x_2)\)的\(k\),要满足\([k+1,i]\)没有前向边
\([x_2,i)\)的\(k\),要满足\([k+1,i]\)有且仅有一条前向边
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=2e5+5,MOD=998244353;
int n,k;
int head[N],cnt=1,fa[N],dp[N][405];
struct node{
int nxt,v;
}tree[N<<1];
void add(int u,int v){
tree[++cnt]={head[u],v},head[u]=cnt;
tree[++cnt]={head[v],u},head[v]=cnt;
}
void dfs(int u){
for(int i=head[u],v;i;i=tree[i].nxt){
if(fa[u]==(v=tree[i].v)) continue;
fa[v]=u,dfs(v);
}
}
int ad(int x,int y){
x+=y;
if(x>=MOD) x-=MOD;
if(x<0) x+=MOD;
return x;
}
int c[N],tr[N];
vector<int> sum[2][405],id[2];
set<int> pos;
int suf[N],top;
int main(){
scanf("%d%d",&n,&k),++n,add(1,2);
for(int i=1,u,v;i<n-1;++i) scanf("%d%d",&u,&v),++u,++v,add(u,v);
for(int i=1;i<=n;++i) pos.insert(i);
fa[2]=1,dfs(2);
id[0].pb(0),id[1].pb(0);
for(int i=0;i<=k;++i) sum[0][i].pb(0),sum[1][i].pb(0);
dp[1][0]=1,sum[0][0].pb(1),id[0].pb(1);
for(int i=1;i<=k;++i) sum[0][i].pb(0);
tr[1]=1;
for(int i=2,x1,x2;i<=n;++i){
if(fa[i]>i) suf[++top]=i;
if(fa[i]<i){
if(pos.size()&&(*prev(pos.end()))>=fa[i])
for(auto it=pos.lower_bound(fa[i]);it!=pos.end()&&(*it)<i;){
++c[*it];
if(c[*it]>1){
id[1].pop_back();
for(int j=0;j<=k;++j) sum[1][j].pop_back();
it=pos.erase(it);
}else ++it;
}
if(pos.size()&&(*prev(pos.end()))>=fa[i])
for(auto it=pos.lower_bound(fa[i]);it!=pos.end()&&(*it)<i;++it){
id[0].pop_back(),id[1].pb(*it);
for(int j=0;j<=k;++j) sum[0][j].pop_back(),sum[1][j].pb(ad(sum[1][j].back(),dp[*it][j]));
tr[*it]=sum[1][0].size()-1;
}
}
while(top&&fa[suf[top]]<=i) --top;
if(!top) x1=x2=1;
else{
x2=suf[top--];
while(top&&fa[suf[top]]<=i) --top;
if(top) x1=suf[top]; else x1=1;
suf[++top]=x2;
}
int l1,r1,l2,r2;
if(*(id[0].end()-1)<x1||x1>x2-1) l1=r1=0;
else l1=tr[*lower_bound(id[0].begin(),id[0].end(),x1)],r1=tr[*(upper_bound(id[0].begin(),id[0].end(),x2-1)-1)];
if(*(id[1].end()-1)<x2||x2>i-1) l2=r2=0;
else l2=tr[*lower_bound(id[1].begin(),id[1].end(),x2)],r2=tr[*(upper_bound(id[1].begin(),id[1].end(),i-1)-1)];
for(int j=1;j<=k;++j){
dp[i][j]=ad(ad(sum[0][j-1][r1],-sum[0][j-1][max(0,l1-1)]),ad(sum[1][j-1][r2],-sum[1][j-1][max(0,l2-1)]));
sum[0][j].pb(ad(sum[0][j].back(),dp[i][j]));
// if(j<=i-1) cout<<i<<" "<<j<<" "<<dp[i][j]<<"----\n"<<x1<<" "<<x2<<" "<<l1<<" "<<r1<<" "<<l2<<" "<<r2<<"\n"<<ad(sum[0][j-1][r1],-sum[0][j-1][max(0,l1-1)])<<" "<<ad(sum[1][j-1][r2],-sum[1][j-1][max(0,l2-1)])<<endl;
}
sum[0][0].pb(sum[0][0].back()),id[0].pb(i),tr[i]=sum[0][0].size()-1;
}
for(int i=1;i<=k;++i) printf("%d\n",dp[n][i]);
return 0;
[gym103860D]Tree Partition的更多相关文章
- [LeetCode] Equal Tree Partition 划分等价树
Given a binary tree with n nodes, your task is to check if it's possible to partition the tree to tw ...
- 663. Equal Tree Partition 能否把树均分为求和相等的两半
[抄题]: Given a binary tree with n nodes, your task is to check if it's possible to partition the tree ...
- [leetcode-663-Equal Tree Partition]
Given a binary tree with n nodes, your task is to check if it's possible to partition the tree to tw ...
- [LeetCode] 663. Equal Tree Partition 划分等价树
Given a binary tree with n nodes, your task is to check if it's possible to partition the tree to tw ...
- 【LeetCode】663. Equal Tree Partition 解题报告 (C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 日期 题目地址:https://leetcode ...
- The 2019 ICPC Asia Shanghai Regional Contest H Tree Partition k、Color Graph
H题意: 给你一个n个节点n-1条无向边构成的树,每一个节点有一个权值wi,你需要把这棵树划分成k个子树,每一个子树的权值是这棵子树上所有节点权值之和. 你要输出这k棵子树的权值中那个最大的.你需要让 ...
- python 常忘代码查询 和autohotkey补括号脚本和一些笔记和面试常见问题
笔试一些注意点: --,23点43 今天做的京东笔试题目: 编程题目一定要先写变量取None的情况.今天就是因为没有写这个边界条件所以程序一直不对.以后要注意!!!!!!!!!!!!!!!!!!!!! ...
- LeetCode All in One题解汇总(持续更新中...)
突然很想刷刷题,LeetCode是一个不错的选择,忽略了输入输出,更好的突出了算法,省去了不少时间. dalao们发现了任何错误,或是代码无法通过,或是有更好的解法,或是有任何疑问和建议的话,可以在对 ...
- All LeetCode Questions List 题目汇总
All LeetCode Questions List(Part of Answers, still updating) 题目汇总及部分答案(持续更新中) Leetcode problems clas ...
- Leetcode problems classified by company 题目按公司分类(Last updated: October 2, 2017)
All LeetCode Questions List 题目汇总 Sorted by frequency of problems that appear in real interviews. Las ...
随机推荐
- 『Plotly实战指南』--柱状图绘制高级篇
在数据可视化的世界里,柱状图是一种直观且强大的工具,用于展示数据的分布.比较和趋势. 从基础的柱状图出发,我们可以进一步探索更复杂的图表类型,如分组柱状图和堆积柱状图,它们在处理多维数据和复杂关系时具 ...
- MySQL 的 JSON 查询
MySQL 的 JSON 路径格式 MySQL 使用特定的 JSON 路径表达式语法来导航和提取 JSON 文档中的数据 基本结构 MySQL 中的 JSON 路径遵循以下通用格式 $[路径组件] 路 ...
- 【Java】String字符串格式化
一.前言 String.format() 作为文本处理工具,为我们提供强大而丰富的字符串格式化功能,为了不止步于简单调用 String.format("Hello %s", &qu ...
- 【Web】Servlet基本概念
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据, ...
- AQS的acquire(int arg) 方法底层源码
一.定义 acquire(int arg) 是 AQS(AbstractQueuedSynchronizer)中的一个核心方法,用于在独占模式下获取同步状态.如果当前线程无法获取同步状态,则将其加入等 ...
- Sentinel源码—7.参数限流和注解的实现
大纲 1.参数限流的原理和源码 2.@SentinelResource注解的使用和实现 1.参数限流的原理和源码 (1)参数限流规则ParamFlowRule的配置Demo (2)ParamFlowS ...
- 使用IDEA管理服务器Docker及远程仓库
目录 配置连接Docker服务器及远程仓库 连接服务器Docker 远程仓库(可选) IDEA管理 确保docker服务器已经开启了远程守护进程访问.[1] 配置连接Docker服务器及远程仓库 连接 ...
- C#/.NET/.NET Core技术前沿周刊 | 第 37 期(2025年5.1-5.11)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...
- net core mvc6使用jwt实现简单的登录
创建一个新的.NET Core Web应用程序项目.在创建项目时,选择MVC模板. 在项目中添加Microsoft.AspNetCore.Authentication.JwtBearer包: 在Vis ...
- argparse基本功能极简介绍
argparse基本功能极简介绍 python脚本文件可以通过命令行的方式调用,在这种调用方法中,可以通过sys.argv来把命令行参数传入脚本文件,通过这种方式传入的参数是string,并且需要将该 ...