文章目录
Codeforces Round #672 (Div. 2)
C1. Pokémon Army (easy version)
题意: 给定n个数字,可以从这n个数字中选择k个,使得 r e s = a 1 − a 2 + a 3 − a 4 + . . . + a k res = a_1 - a_2 + a_3 - a_4 +... + a_k res=a1−a2+a3−a4+...+ak,要求找到合适的k,使得res最大。 1 < = n < = 3 ∗ 1 0 5 1<=n<=3*10^5 1<=n<=3∗105
题解: 状态机dp。 f [ i ] [ 0 ] f[i][0] f[i][0]:选择完前i个物品且最后一个加入的数字前面是减号的最大值; f [ i ] [ 1 ] f[i][1] f[i][1]:选择完前i个物品且最后一个加入的数字前面是加号的最大值。
因此转移方程为:
f [ i ] [ 1 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 1 ] [ 0 ] + a [ i ] , a [ i ] ) f[i][1] = max({f[i - 1][1], f[i - 1][0] + a[i], a[i]}) f[i][1]=max(f[i−1][1],f[i−1][0]+a[i],a[i])
f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 1 ] − a [ i ] ) f[i][0] = max(f[i - 1][0], f[i - 1][1] - a[i]) f[i][0]=max(f[i−1][0],f[i−1][1]−a[i])
答案为: m a x ( f [ n ] [ 0 ] , f [ n ] [ 1 ] ) max(f[n][0], f[n][1]) max(f[n][0],f[n][1])
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int const MAXN = 3e5 + 10;
int n, m, T, a[MAXN], f[MAXN][2];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> T;
while(T--) {
int q;
cin >> n >> q;
for (int i =1 ; i <= n; ++i) cin >> a[i], f[i][0] = f[i][1] = 0;
for (int i = 1; i <= n; ++i) {
f[i][1] = max({f[i - 1][1], f[i - 1][0] + a[i], a[i]});
f[i][0] = max(f[i - 1][0], f[i - 1][1] - a[i]);
}
cout << max(f[n][1], f[n][0]) << endl;
}
return 0;
}
/*
3
3 0
1 3 2
2 0
1 2
7 0
1 2 5 4 3 6 7
*/
C2. Pokémon Army (hard version)
题意: 给定n个数字,可以从这n个数字中选择k个,使得 r e s = a 1 − a 2 + a 3 − a 4 + . . . + a k res = a_1 - a_2 + a_3 - a_4 +... + a_k res=a1−a2+a3−a4+...+ak,要求找到合适的k,使得res最大。同时,有q次操作,每次操作输入一个l和r,然后交换a[l]和a[r],再次询问res的最大值。 1 < = n < = 3 ∗ 1 0 5 1<=n<=3*10^5 1<=n<=3∗105
题解: 线段树维护区间最大值:维护数组 f [ 2 ] [ 2 ] f [ 2 ] [ 2 ] f[2][2]
f
[
0
]
[
0
]
f[0][0]
f[0][0]表示序列开头+,结尾+。
f
[
0
]
[
1
]
f [ 0 ] [ 1 ]
f[0][1] 表示序列开头+,结尾-。
f
[
1
]
[
0
]
f [ 1 ] [ 0 ]
f[1][0]表示序列开头-,结尾+。
$f [ 1 ] [ 1 ]
表
示
序
列
开
头
−
,
结
尾
−
。
每
次
进
行
交
换
操
作
后
,
进
行
暴
力
修
改
,
重
新
维
护
f
数
组
即
可
,
然
后
打
印
表示序列开头-,结尾-。 每次进行交换操作后,进行暴力修改,重新维护f数组即可,然后打印
表示序列开头−,结尾−。每次进行交换操作后,进行暴力修改,重新维护f数组即可,然后打印f[0].val$
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 300010;
const LL INF = 1e17;
LL a[N];
int n, q;
// f[0][0] ++
// f[0][1] +-
// f[1][0] -+
// f[1][1] --
struct node {
int l, r;
LL val;
LL f[2][2];
} tree[N * 4];
void pushup(int u) {
tree[u].val = -INF;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++) {
tree[u].f[i][j] =
max(tree[u << 1].f[i][j], tree[u << 1 | 1].f[i][j]);
tree[u].f[i][j] =
max(tree[u].f[i][j],
max(tree[u << 1].f[i][0] + tree[u << 1 | 1].f[1][j],
tree[u << 1].f[i][1] + tree[u << 1 | 1].f[0][j]));
tree[u].val = max(tree[u].val, tree[u].f[i][j]);
}
}
void build(int u, int l, int r) {
tree[u] = {l, r};
if (l == r) {
tree[u].val = a[l];
tree[u].f[0][0] = a[l];
tree[u].f[1][1] = -a[l];
tree[u].f[0][1] = tree[u].f[1][0] = -INF;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int p, int x) {
if (tree[u].l == tree[u].r) {
tree[u].val = x;
tree[u].f[0][0] = x;
tree[u].f[1][1] = -x;
tree[u].f[0][1] = tree[u].f[1][0] = -INF;
return;
}
int mid = tree[u].l + tree[u].r >> 1;
if (p <= mid)
modify(u << 1, p, x);
else
modify(u << 1 | 1, p, x);
pushup(u);
}
int main() {
ios::sync_with_stdio(false);
cout.tie(0);
int T;
cin >> T;
while (T--) {
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
cout << tree[1].val << '\n';
while (q--) {
int l, r;
cin >> l >> r;
modify(1, l, a[r]);
modify(1, r, a[l]);
cout << tree[1].val << '\n';
swap(a[l], a[r]);
}
}
return 0;
}
D. Rescue Nibel!
题意: 给出 n 盏灯,每盏灯在 [ l , r ] 这段时间内会保持点亮的状态,问恰好有 k 盏灯同时点亮,有多少种组合方式。 1 < = n < = 3 ∗ 1 0 5 1<=n<=3*10^5 1<=n<=3∗105
题解: 组合计数+扫描线思想。先把每个区间的左端点和右端点拆开记录下来,然后排个序(注意:先按照位置排序,按照左端点优先原则排序)借用扫描线的思想扫描整个序列。维护一个cnt,表示到当前这个点时,在它的区间的右端点的数目。扫描的过程中,如果到一个左端点说明目前有一盏灯在此时刻点亮那么对答案的贡献就是在需要从前面点亮的灯中选择k − 1盏灯,维护前面点亮灯的数量只需要维护一个cnt即可,那么对答案的贡献即 C c n t k − 1 C_{cnt}^{k-1} Ccntk−1,然后更新cnt即可,如果扫描到一个右端点不难发现对答案没有贡献,只需要维护cnt即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 300010;
const LL mod = 998244353;
PII a[2 * N];
int n, k;
LL qmi(LL a, LL b, LL p) {
LL res = 1;
while (b) {
if (b & 1) res = res * a % p;
b >>= 1;
a = a * a % p;
}
return res;
}
LL fact[N], infact[N];
void init(int n) {
fact[0] = infact[0] = 1;
for (int i = 1; i <= n; i++) {
fact[i] = fact[i - 1] * i % mod;
infact[i] = qmi(fact[i], mod - 2, mod);
}
}
int main() {
cin >> n >> k;
init(n);
for (int i = 1; i <= n; i++) {
int l, r;
cin >> l >> r;
a[i].first = l, a[i].second = -1; //左端点
a[i + n].first = r, a[i + n].second = 1; //右端点
}
sort(a + 1, a + 1 + 2 * n);
LL res = 0;
LL cnt = 0;
for (int i = 1; i <= 2 * n; i++) {
int p = a[i].first, id = a[i].second;
if (id == -1) {
if (cnt >= k - 1)
res = (res + fact[cnt] * infact[k - 1] % mod *
infact[cnt - k + 1] % mod) %
mod;
cnt++;
} else
cnt--;
}
cout << res << '\n';
return 0;
}