好久没写cf题解了,这场cf写了两个线段树,其中还有一个竟然写搓了,导致没能上橙,5555,看来省赛前是注定不能橙了。
D. Restore Permutation
解法:我们从小到大枚举 i i i,然后从线段树中找到最右边的一个0的位置 k k k然后 a [ k ] = i a[k]=i a[k]=i,然后我们清空这个值,然后在线段树中将区间 [ k + 1 , r ] [k +1, r] [k+1,r]减去 i i i即可, 线段树入门题
#include
#define ll long long
using namespace std;
const int maxn = 2e5 + 10;
ll mn[maxn * 4], tag[maxn * 4], p[maxn * 4], a[maxn];
#define ls o * 2
#define rs o * 2 + 1
#define m (l + r) / 2
void pushup(int o) {
if (mn[ls] < mn[rs])
mn[o] = mn[ls], p[o] = p[ls];
else
mn[o] = mn[rs], p[o] = p[rs];
}
void pushdown(int o) {
tag[ls] += tag[o]; tag[rs] += tag[o];
mn[ls] += tag[o]; mn[rs] += tag[o];
tag[o] = 0;
}
void build(int o, int l, int r) {
if (l == r) {
scanf("%lld", &mn[o]);
p[o] = l;
return;
}
build(ls, l, m);
build(rs, m + 1, r);
pushup(o);
}
void up(int o, int l, int r, int ql, int qr, ll v) {
if (ql > qr)
return;
if (l >= ql && r <= qr) {
mn[o] += v;
tag[o] += v;
return;
}
pushdown(o);
if (ql <= m)
up(ls, l, m, ql, qr, v);
if (qr > m)
up(rs, m + 1, r, ql, qr, v);
pushup(o);
}
int main() {
int n;
scanf("%d", &n);
build(1, 1, n);
for (int i = 1; i <= n; i++) {
a[p[1]] = i;
int k = p[1];
up(1, 1, n, k, k, 1e11);
up(1, 1, n, k + 1, n, -i);
}
for (int i = 1; i <= n; i++)
printf("%lld ", a[i]);
}
E. Let Them Slide
线段树暴力解法:每一行的第 i i i个元素 x i x_{i} xi,我们用线段树在区间 [ i , n − l e n + i ] [i, n - len + i] [i,n−len+i]去和 x i x_{i} xi取max,每一行处理完后,在线段树中暴力更新这一行数对答案的贡献,暴力更新直到某个区间最大值等于最小值就不用继续往下更新了,线段树骚操作题
#include
#define ll long long
using namespace std;
const int maxn = 1e6 + 10, inf = 1e9 + 1;
int mn[maxn * 4], tag[maxn * 4], mx[maxn * 4], vis[maxn * 4];
ll sum[maxn * 4];
vector<int> G;
#define ls o * 2
#define rs o * 2 + 1
#define m (l + r) / 2
void pushup(int o) {
mx[o] = max(mx[ls], mx[rs]);
mn[o] = min(mn[ls], mn[rs]);
}
void add(int o) {
if (!vis[o])
G.push_back(o);
vis[o] = 1;
}
void pushdown(int o) {
if (tag[o] != -inf) {
tag[ls] = max(tag[o], tag[ls]);
tag[rs] = max(tag[o], tag[rs]);
mn[ls] = max(mn[ls], tag[o]);
mn[rs] = max(mn[rs], tag[o]);
mx[ls] = max(mx[ls], tag[o]);
mx[rs] = max(mx[rs], tag[o]);
add(ls);
add(rs);
tag[o] = -inf;
}
}
void up(int o, int l, int r, int ql, int qr, int v) {
if (ql > qr || mn[o] >= v)
return;
add(o);
if (l >= ql && r <= qr) {
if (mx[o] <= v) {
mx[o] = mn[o] = tag[o] = v;
return;
}
if (mn[o] >= v)
return;
pushdown(o);
if (mn[ls] < v)
up(ls, l, m, ql, qr, v);
if (mn[rs] < v)
up(rs, m + 1, r, ql, qr, v);
pushup(o);
return;
}
pushdown(o);
if (ql <= m)
up(ls, l, m, ql, qr, v);
if (qr > m)
up(rs, m + 1, r, ql, qr, v);
pushup(o);
}
ll qu(int o, int l, int r, int k) {
if (l == r)
return sum[o];
ll ans = sum[o];
if (k <= m)
ans += qu(ls, l, m, k);
else
ans += qu(rs, m + 1, r, k);
return ans;
}
void gao(int o, int l, int r) {
if (mx[o] == mn[o]) {
sum[o] += mx[o];
return;
}
gao(ls, l, m);
gao(rs, m + 1, r);
}
int main() {
int q, n, l, x;
scanf("%d%d", &q, &n);
for (int i = 1; i <= 4 * n; i++)
mx[i] = mn[i] = tag[i] = -inf;
for (int i = 1; i <= q; i++) {
scanf("%d", &l);
for (int j = 1; j <= l; j++) {
scanf("%d", &x);
up(1, 1, n, j, n - (l - j), x);
}
if (l < n) {
up(1, 1, n, l + 1, n, 0);
up(1, 1, n, 1, n - l, 0);
}
gao(1, 1, n);
for (auto o : G)
mx[o] = mn[o] = tag[o] = -inf, vis[o] = 0;
G.clear();
}
for (int i = 1; i <= n; i++)
printf("%lld ", qu(1, 1, n, i));
}
F. Bits And Pieces
解法:我们从后面往前面枚举 a i a_{i} ai对答案的贡献,我们用一个 c n t cnt cnt存所有 i i i后面的数的所有子集出现次数,然后从高位枚举 a i a_{i} ai二进制为0的位置 j j j,贪心的用 c n t cnt cnt判断其是否出现了超过或等于2次,如果出现次数管够,我们贪心的将 a i ∣ ( 1 < < j ) ai|(1<<j) ai∣(1<<j),最后和答案取max即可
#include
using namespace std;
const int maxn = 1 << 21;
int cnt[maxn], vis[maxn], a[maxn];
void insert(int x, int id) {
if (cnt[x] == 2 || vis[x] == id)
return;
cnt[x]++;
vis[x] = id;
for (int i = 20; ~i; i--)
if (x >> i & 1)
insert(x ^ (1 << i), id);
}
int main() {
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int m = maxn - 1;
for (int i = n; i; i--) {
int x = m ^ a[i];
int s = 0;
for (int j = 20; ~j; j--)
if (x >> j & 1) {
if (cnt[s | (1 << j)] == 2)
s |= (1 << j);
}
if (i <= n - 2)
ans = max(ans, a[i] | s);
insert(a[i], i);
}
printf("%d\n", ans);
}