260305 模拟赛
T1
给定一个序列上的若干区间,区间有权值。你需要将序列划分为恰好 \(k\) 段,一段的权值为包含于它的区间的权值和。对 \(k\in [1,n]\) 求出最大权值和。
\(n\le 5000\)
直接优化 dp 转化为前缀加正整数前缀查。发现我们只关心上升链,于是用链表和并查集维护它即可。时间复杂度 \(O(n^2)\)。
代码
| #include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 1e4 + 10;
const ll INF = 0x3f3f3f3f3f3f3f3f;
struct myPair {
int p, v;
};
int T, n, m;
ll ans[N];
ll f[2][N];
vector<myPair> op[N];
int fa[N], nxt[N]; ll d[N];
inline int find(int x) { if(fa[x] == x) return x; return fa[x] = find(fa[x]); }
inline void clear() {
for(int i = 0; i <= n + 2; i++) op[i].clear();
for(int i = 0; i <= n + 2; i++) f[0][i] = -INF;
}
int main() {
freopen("segment.in", "r", stdin);
freopen("segment.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while(T--) {
cin >> n >> m; n++;
clear();
for(int i = 1; i <= m; i++) {
int l, r, v;
cin >> l >> r >> v;
op[r + 1].push_back({l, v});
}
f[0][0] = 0;
for(int k = 1; k <= n - 1; k++) {
int kk = (k & 1) ^ 1;
for(int i = 0; i <= n; i++) f[k & 1][i] = -INF, fa[i] = i, d[i] = 0, nxt[i] = 0;
d[0] = -f[kk][0];
for(int i = 1; i <= n; i++) {
for(myPair &o : op[i]) {
int p = find(o.p);
d[p] -= o.v;
while(nxt[p] && d[p] <= 0) {
d[p] += d[nxt[p]];
fa[nxt[p]] = p;
nxt[p] = nxt[nxt[p]];
}
}
int j = find(i - 1);
f[k & 1][i] = -d[j];
if(f[kk][i] > -d[j]) {
nxt[j] = i;
d[j] += f[kk][i];
d[i] = -f[kk][i];
} else {
fa[i] = j;
}
}
ans[n - k] = f[k & 1][n];
}
for(int i = 1; i <= n - 1; i++) cout << ans[i] << ' '; cout << '\n';
}
return 0;
}
|
T2
是那个两个斯特林数,两个容斥的那个原题。