CF - 558E
题意: 给定一个字符串, q次操作, 每次操作一个区间, 0表示让这个区间降序, 1 表示升序, 问最后字符串的样子.
思路: 区间排序问题, 凡是区间排序问题, 并且里面涉及到的种类不多大概60左右, 都是可以用线段树维护的, 并且种类越少越好排, 所以我们直接用线段树维护当前这个区间每个种类的数量, 这里只有26种, 然后对于每次排序, 我们首先计算出这个区间的每一种种类数量, 然后再一一区间赋值, 赋值回去就很好的维护好了.
AC Code
const int maxn = 1e5+5;
char a[maxn];
struct node{
int tl, tr; ll v[26], lazy;
void fun(ll tmp) {
lazy = tmp;
for (int i = 0 ; i < 26 ; i ++) v[i] = 0;
v[tmp] = tr - tl + 1;
}
} tre[maxn<<2];
void pushup(int id) {
for (int i = 0 ; i < 26 ; i ++)
tre[id].v[i] = tre[id<<1].v[i] + tre[id<<1|1].v[i];
}
void pushdown(int id) {
if(tre[id].lazy != -1) {
tre[id<<1].fun(tre[id].lazy);
tre[id<<1|1].fun(tre[id].lazy);
tre[id].lazy = -1;
}
}
void build(int id,int l,int r) {
tre[id].tl = l; tre[id].tr = r; tre[id].lazy = -1;
for (int i = 0 ; i < 26 ; i ++) tre[id].v[i] = 0;
if(l == r) {
tre[id].v[a[l]-'a'] = 1;
return ;
}
int mid = (l+r) >> 1;
build(id<<1, l, mid);
build(id<<1|1, mid+1, r);
pushup(id);
}
void update(int id, int ul, int ur, int val) {
int l = tre[id].tl, r = tre[id].tr;
if(ul <= l && r <= ur) {
tre[id].fun(val);
return ;
}
pushdown(id);
int mid = (l+r) >> 1;
if(ul <= mid) update(id<<1, ul, ur, val);
if(ur > mid) update(id<<1|1, ul, ur, val);
pushup(id);
}
int query(int id, int ql, int qr, int f, int ans) {
int l = tre[id].tl , r = tre[id].tr;
if(ql <= l && r <= qr) {
if (ans) {
for (int i = 0 ; i < 26 ; i ++) {
if (tre[id].v[i]) return i;
}
}
return tre[id].v[f];
}
pushdown(id);
int mid = (l+r) >> 1 ;
if(qr <= mid) return query(id<<1, ql, qr, f, ans);
else if(ql > mid) return query(id<<1|1, ql, qr, f, ans);
else return query(id<<1, ql, mid, f, ans) + query(id<<1|1, mid+1, qr, f, ans);
}
int cnt[26];
void solve() {
int n, q;
while(~scanf("%d%d", &n, &q)) {
scanf("%s", a+1); build(1, 1, n);
while(q--) {
int l, r, op;
scanf("%d%d%d", &l, &r, &op);
for (int i = 0 ; i < 26 ; i ++) {
cnt[i] = query(1, l, r, i, 0);
}
if (op) {
for (int i = 0 ; i < 26 ; i ++) {
if (!cnt[i]) continue;
update(1, l, l+cnt[i]-1, i);
l += cnt[i];
}
}
else {
for (int i = 25 ; i >= 0 ; i --) {
if (!cnt[i]) continue;
update(1, l, l+cnt[i]-1, i);
l += cnt[i];
}
}
}
for (int i = 1 ; i <= n ; i ++) {
printf("%c", query(1, i, i, 0, 1)+'a');
}
puts("");
}
}
UESTC - 1919
题意: 每次给出n个数字(是n的一个排列), 和一个k, 然后进行q次操作,每次操作排序一个区间, 问最后的数组的第k位上面的数字是多少.
思路:因为前面已经说过了, 种类越少越好排, 怎么压缩种类了?我们再看一下是否具有二分性质?假设我们二分了一个答案, 我们将这个数组中>=ans的赋值为1, 否则赋值为0, 然后这只有两种种类了, 进行q次排序肯定很好维护, 那么怎么check? 如果最后第k位上是1, 那么当前答案肯定是满足的, 因为>=都是1, 那么比ans小的数满足嘛?肯定也是满足的, 因为假设这个就是答案, 那么更小的数也会使这个答案在第k位时依旧时1, 所以满足二分性质, 然后二分套线段树维护即可.
AC Code
const int maxn = 1e5+5;
int a[maxn];
struct node{
int tl, tr; ll v[2], lazy;
void fun(ll tmp) {
lazy = tmp;
v[tmp] = tr - tl + 1;
v[tmp^1] = 0;
}
} tre[maxn<<2];
void pushup(int id) {
for (int i = 0 ; i < 2 ; i ++)
tre[id].v[i] = tre[id<<1].v[i] + tre[id<<1|1].v[i];
}
void pushdown(int id) {
if(tre[id].lazy != -1) {
tre[id<<1].fun(tre[id].lazy);
tre[id<<1|1].fun(tre[id].lazy);
tre[id].lazy = -1;
}
}
void build(int id,int l,int r, int x) {
tre[id].tl = l; tre[id].tr = r; tre[id].lazy = -1;
tre[id].v[0] = tre[id].v[1] = 0;
if(l == r) {
tre[id].v[a[l]>=x] = 1;
return ;
}
int mid = (l+r) >> 1;
build(id<<1, l, mid, x);
build(id<<1|1, mid+1, r, x);
pushup(id);
}
void update(int id, int ul, int ur, int val) {
int l = tre[id].tl, r = tre[id].tr;
if(ul <= l && r <= ur) {
tre[id].fun(val);
return ;
}
pushdown(id);
int mid = (l+r) >> 1;
if(ul <= mid) update(id<<1, ul, ur, val);
if(ur > mid) update(id<<1|1, ul, ur, val);
pushup(id);
}
int query(int id, int ql, int qr, int f) {
int l = tre[id].tl , r = tre[id].tr;
if(ql <= l && r <= qr) {
return tre[id].v[f];
}
pushdown(id);
int mid = (l+r) >> 1 ;
if(qr <= mid) return query(id<<1, ql, qr, f);
else if(ql > mid) return query(id<<1|1, ql, qr, f);
else return query(id<<1, ql, mid, f) + query(id<<1|1, mid+1, qr, f);
}
struct QQ {
int o, l, r;
}op[maxn];
void solve() {
int n, k;
while(~scanf("%d%d", &n, &k)) {
for(int i = 1 ; i <= n ; i ++) {
scanf("%d", &a[i]);
}
int q; scanf("%d", &q);
for (int i = 1 ; i <= q ; i ++) {
scanf("%d%d%d", &op[i].o, &op[i].l, &op[i].r);
}
int l = 1, r = n, ans = -1;
while(r >= l) {
int mid = (l + r) >> 1;
build(1, 1, n, mid);
for (int i = 1 ; i <= q ; i ++) {
if (!op[i].o) {
int num = query(1, op[i].l, op[i].r, 0);
if (!num || num == op[i].r - op[i].l + 1) continue;
update(1, op[i].l, op[i].l+num-1, 0);
update(1, op[i].l+num, op[i].r, 1);
}
else {
int num = query(1, op[i].l, op[i].r, 1);
if (!num || num == op[i].r - op[i].l + 1) continue;
update(1, op[i].l, op[i].l+num-1, 1);
update(1, op[i].l+num, op[i].r, 0);
}
}
if (query(1, k, k, 1)) {
ans = mid;
l = mid+1;
}
else r = mid - 1;
}
printf("%d\n", ans);
}
}