题解:P17140 [NOI 2026] 线段
freematt_matt · · 题解
题外话:本人场上写了时空均为
首先容易发现不可能有一个点被不少于
可以把所选的线段里右端点最靠右的看成这个树的根,那么它的儿子之间不能相交,并且它的儿子里只有最靠左的儿子不是叶子,并且不能和孙子有交点,否则都会出现环。想象一下,差不多会变成下面这个结构:
发现所有右端点都是递增的。于是可以先把所有线段按右端点大小升序排序。然后右端点必须与父亲相交。
考虑 DP 。记
然后随便处理一下边界,开个滚动数组就做完了。
#include <bits/stdc++.h>
#include "segment.h"
using namespace std;
const int N = 3010, fff=998244353;
void init(int c, int t) {
return;
}
struct node {
int l, r;
}b[N];
bool cmp(node x, node y) {
return x.r<y.r;
}
int dp[3010][1010][2], ds[N];
vector<int> segment(int n, int m, int k, vector<int> l, vector<int> r) {
vector<int> a;
for(int i=0;i<=k;i++)a.push_back(0);
for(int i=0;i<n;i++) {
b[i+1]={l[i], r[i]};
}
sort(b+1,b+n+1,cmp);
int pos=0;
for(int i=1;i<=m;i++) {
//找r < i的
while(pos<n&&b[pos+1].r<i)++pos;
ds[i]=pos;
}
for(int p=0;p<=k;p++) {
for(int i=0;i<=n;i++) {
for(int j=0;j<=m;j++) {
if(i==0) {
if(p==0)dp[i][j][p&1]=1;
else dp[i][j][p&1]=0;
continue;
}
dp[i][j][p&1]=dp[i-1][j][p&1];
if(p==0)continue;
//决定i要不要选
if(j==0) {
dp[i][j][p&1]+=dp[i-1][b[i].l][(p&1)^1], dp[i][j][p&1]%=fff;
continue;
}
if(b[i].r<j)continue;
if(b[i].l>j) {
int d=ds[b[i].l];
dp[i][j][p&1]+=dp[d][j][(p&1)^1], dp[i][j][p&1]%=fff;
}else {
int d=ds[j];
dp[i][j][p&1]+=dp[d][b[i].l][(p&1)^1], dp[i][j][p&1]%=fff;
}
}
}
if(p>=1&&p<=k)a[p]=dp[n][0][p&1];
}
return a;
}