题解 排序
首先有一个暴力是在每个节点维护所有可能的上升序列,用一个区间表示
发现有包含关系的区间中主动包含的那个一定不优,剪掉即可
发现合并时的 合并可以用状压,按照加入顺序 DP 优化
( 表示已选的儿子为 时的又端点最小值)
发现如果当前节点只有一个儿子,信息是可以 继承的
好的现在我们来证明这个东西的复杂度是对的(((
大意是将原树按每个子树包含的 数量轻重链剖分
考虑每个节点的合法线段数量
发现每条合法线段的两个端点中至少有一个不属于重儿子
发现任意一个可能作为端点的点最多只能作为一个合法区间的端点
所以合法线段数量不超过
也就是每个叶子上的数字只在向上跳到链顶时产生一次贡献
那么总贡献是 级别的
所以总复杂度是( 与 同级)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
bool leaf[N];
vector<int> buc[N];
int head[N], sta[N], deg[N], top, ecnt;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}
#if 0
namespace force{
int val[N];
vector<int> rec[N];
vector<int> dfs1(int u) {
int tot=0;
vector<int> now, son[8], id;
rec[u].clear();
if (leaf[u]) {now.pb(val[u]); return now;}
for (auto v:e[u]) {
son[tot++]=dfs1(v);
rec[u].pb(tot-1);
id.pb(v);
}
sort(rec[u].begin(), rec[u].end(), [&](int a, int b){return son[a][0]<son[b][0];});
for (auto it:rec[u]) {for (auto t:son[it]) now.pb(t);}
for (int i=0; i<rec[u].size(); ++i) rec[u][i]=id[rec[u][i]];
return now;
}
void check() {
vector<int> t=dfs1(1);
int lst=0;
for (auto it:t) {
if (it<=lst) return ;
lst=it;
}
puts("Yes");
for (int i=1; i<=n; ++i)
if (leaf[i]) printf("%d\n", val[i]);
else {for (auto it:rec[i]) printf("%d ", it); printf("\n");}
exit(0);
}
void dfs2(int u) {
if (u>top) {check(); return ;}
for (auto it:buc[sta[u]]) {
val[sta[u]]=it;
dfs2(u+1);
}
}
void solve() {
dfs2(1);
puts("No");
exit(0);
}
}
#endif
namespace task{
int f[1<<8];
vector<int> ans[N];
vector<pair<int, int>> tem[N], g[1<<8];
vector<vector<pair<int, int>>> rec[N];
struct tpl{int l, r, bel, rk;};
vector<tpl> tp[N];
inline bool operator < (tpl a, tpl b) {return a.l<b.l;}
void dfs1(int u) {
// cout<<"dfs: "<<u<<endl;
int son[8], tot=0;
if (leaf[u]) {for (auto it:buc[u]) tem[u].pb({it, it}); sort(tem[u].begin(), tem[u].end()); return ;}
if (deg[u]==1) {int v=e[head[u]].to; dfs1(v); swap(tem[u], tem[v]); swap(rec[u], rec[v]); return ;}
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
dfs1(v);
son[tot++]=v;
int cnt=0;
for (auto it:tem[v]) tp[u].pb({it.fir, it.sec, tot-1, cnt++});
}
// cout<<"calc: "<<u<<endl;
// cout<<"son: "; for (int i=0; i<tot; ++i) cout<<son[i]<<' '; cout<<endl;
sort(tp[u].begin(), tp[u].end());
vector<pair<pair<int, int>, vector<pair<int, int>>>> all;
int lim=1<<tot;
for (auto it:tp[u]) {
for (int s=0; s<lim; ++s) if (s&(1<<it.bel)) f[s]=INF, g[s].clear();
f[1<<it.bel]=it.r; g[1<<it.bel].pb({it.bel, it.rk});
for (int s=0; s<lim; ++s) if (s&(1<<it.bel)) {
for (int i=0; i<tot; ++i) if (!(s&(1<<i))) {
auto t=lower_bound(tem[son[i]].begin(), tem[son[i]].end(), make_pair(f[s], 0));
if (t==tem[son[i]].end()) continue;
if (f[s|(1<<i)]>t->sec) {
f[s|(1<<i)]=t->sec;
g[s|(1<<i)]=g[s];
g[s|(1<<i)].pb(make_pair(i, t-tem[son[i]].begin()));
}
}
}
if (f[lim-1]!=INF) all.pb({{it.l, f[lim-1]}, g[lim-1]});
}
sort(all.begin(), all.end());
int lst=0;
for (auto it:all) if (it.fir.fir!=lst) {
lst=it.fir.fir;
while (tem[u].size() && tem[u].back().sec>=it.fir.sec) tem[u].pop_back(), rec[u].pop_back();
tem[u].pb(it.fir); rec[u].pb(it.sec);
}
}
void dfs2(int u, int id) {
int son[8], tot=0;
if (leaf[u]) {ans[u].pb(tem[u][id].fir); return ;}
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
son[tot++]=v;
}
if (deg[u]==1) {
ans[u].pb(son[0]);
swap(tem[u], tem[son[0]]);
swap(rec[u], rec[son[0]]);
dfs2(son[0], id);
return ;
}
for (auto it:rec[u][id]) ans[u].pb(son[it.fir]), dfs2(son[it.fir], it.sec);
}
void solve() {
dfs1(1);
#if 0
cout<<"---tem---"<<endl;
for (int i=1; i<=n; ++i) {for (auto it:tem[i]) cout<<"("<<it.fir<<','<<it.sec<<")"<<' '; cout<<endl;}
cout<<"---rec---"<<endl;
for (int i=1; i<=n; ++i) {for (auto v:rec[i]) {cout<<"{"; for (auto it:v) cout<<"("<<it.fir<<','<<it.sec<<")"; cout<<"} ";} cout<<endl;}
#endif
if (!tem[1].size()) {puts("No"); exit(0);}
puts("Yes");
dfs2(1, 0);
for (int i=1; i<=n; ++i)
if (leaf[i]) printf("%d\n", ans[i][0]);
else {for (auto it:ans[i]) printf("%d ", it); printf("\n");}
}
}
signed main()
{
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
n=read();
memset(head, -1, sizeof(head));
for (int i=1,k; i<=n; ++i) {
if (read()&1) {
deg[i]=k=read();
while (k--) add(i, read());
}
else {
leaf[sta[++top]=i]=1;
k=read();
while (k--) buc[i].pb(read());
}
}
// if (n<=20) force::solve();
// else puts("No");
task::solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)