线段
Mier_Samuelle · · 题解
树 = 连通 + 无环。连通意味着必须与前面选的线段相交,无环意味着只能与一条线段相交。发现这个条件只与已选线段中次大和最大的右端点有关,据此,定义
- 不选
i ,则f_{i,j,x,y} \leftarrow f_{i-1,j,x,y} 。 - 选
i ,且x<l_i \le y \le r_i ,则f_{i,j,y,r_i} \leftarrow f_{i-1,j-1,x,y} 。 - 选
i ,且x<l_i \le r_i < y ,则f_{i,j,r_i,y} \leftarrow f_{i-1,j-1,x,y} 。
转移前将线段按左端点排序。第一维可简单地滚掉,空间
进一步地,发现转移的条件已经决定了线段是按左端点升序选择的,故可以不要
最后,发现转移就是对
:::success[Code]{open}
#include "segment.h"
#include <vector>
#include <algorithm>
#include <cstring>
#define fi first
#define se second
using namespace std;
typedef pair<int, int> pii;
const int MAXN = 3010, MAXM = 1010;
const int MOD = 998244353;
int f[MAXM][MAXM], g[MAXM][MAXM], pre[MAXM][MAXM];
pii a[MAXN];
void add(int &x, int y){
x += y;
if (x >= MOD){
x -= MOD;
}
return;
}
void init(int c, int t){
return;
}
std::vector<int> segment(int n, int m, int k, std::vector<int> l_vec, std::vector<int> r_vec){
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
memset(pre, 0, sizeof(pre));
for (int i = 1; i <= n; i++){
a[i].fi = l_vec[i - 1];
a[i].se = r_vec[i - 1];
}
std::vector<int> res(k + 1, 0);
for (int i = 1; i <= k; i++){
for (int x = 0; x <= m; x++){
for (int y = x; y <= m; y++){
g[x][y] = f[x][y];
f[x][y] = 0;
}
}
if (i == 1){
for (int p = 1; p <= n; p++){
int r = a[p].se;
add(f[0][r], 1);
}
}
else if (i == 2){
for (int p = 1; p <= n; p++){
for (int q = p + 1; q <= n; q++){
if (min(a[p].se, a[q].se) >= max(a[p].fi, a[q].fi)){
int x = min(a[p].se, a[q].se);
int y = max(a[p].se, a[q].se);
add(f[x][y], 1);
}
}
}
}
else{
for (int y = 1; y <= m; y++){
pre[0][y] = 0;
for (int x = 1; x <= y; x++){
pre[x][y] = (pre[x - 1][y] + g[x][y]) % MOD;
}
}
for (int p = 1; p <= n; p++){
int l = a[p].fi, r = a[p].se;
for (int y = l; y <= m; y++){
if (y <= r){
add(f[y][r], pre[l - 1][y]);
}
else{
add(f[r][y], pre[l - 1][y]);
}
}
}
}
int ans = 0;
for (int x = 0; x <= m; x++){
for (int y = x; y <= m; y++){
add(ans, f[x][y]);
}
}
res[i] = ans;
}
return res;
}
:::