A simple simulation problem [HDU4973]
【题目描述】
翻译:初始时按顺序排着一排细胞 第\(i\)个的种类是\(i\)
两种操作 第一种是把当前的第\(l\sim r\)个细胞原地翻倍
第二种是查询当前第\(l\sim r\)个细胞中 数量最多的同种细胞有多少个
\(n\le 5*10^4\),任何时候细胞总数不超过\(5*10^{12}\)
举个例子 原来的细胞序列是\(\{1,2,3,3,4,5\}\) 把\([2,5]\)翻倍 得到\(\{1,2,2,3,3,3,3,4,4,5\}\)
题解
大概1min就想到正解了QAQ(可能是因为这个是线段树专题 所以明示要用线段树) 但是调了1h才AC
非常显然 不管怎么超级加倍这个细胞序列肯定都是一个不降的序列 同种细胞全部都在连续的一段里面
那我们用线段树维护一下每种细胞有多少个 然后不管是修改还是查询 都先在线段树上二分找到第\(l\)个及第\(r\)个位置是什么细胞 不妨设第\(l\)个是\(x\),第\(r\)个是\(y\)
然后我们还要查出来\([l,r]\)中有多少个\(x\)多少个\(y\) 这个其实在线段树二分的时候就可以顺便查到了 我实现的比较毒瘤 用了个pair
。。。
那么种类为\(x+1\sim y-1\)的那些细胞肯定全部都被包含在\([l,r]\)之间了 对于修改操作直接区间给乘个\(2\)就好 查询也就是查一下区间最大值
然后把\(x,y\)单独处理一下 修改操作的话就现在\([l,r]\)区间里有多少个\(x\)(我们刚才已经求过了)就给\(x\)加上多少 查询就比一下大小。。。
完事啦 时间复杂度\(O(n \log n)\)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
inline ll read() {
ll x = 0, f = 1; char ch = getchar();
for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
return x * f;
}
ll ttt, n, m;
char s[5];
struct segtree{
ll l, r, mx, sum, tag;
} tr[200005];
void build(ll ind, ll l, ll r) {
tr[ind].l = l; tr[ind].r = r; tr[ind].tag = 1;
if (l == r) {
tr[ind].mx = tr[ind].sum = 1;
return;
}
ll mid = (l + r) >> 1;
build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
tr[ind].mx = max(tr[ind<<1].mx, tr[ind<<1|1].mx);
tr[ind].sum = tr[ind<<1].sum + tr[ind<<1|1].sum;
}
inline void pushdown(ll ind) {
ll v = tr[ind].tag; tr[ind].tag = 1;
tr[ind<<1].tag *= v; tr[ind<<1].mx *= v; tr[ind<<1].sum *= v;
tr[ind<<1|1].tag *= v; tr[ind<<1|1].mx *= v; tr[ind<<1|1].sum *= v;
}
void update(ll ind, ll pos, ll v) {
ll l = tr[ind].l, r = tr[ind].r;
if (l == r) {
tr[ind].mx += v;
tr[ind].sum += v;
return;
}
pushdown(ind);
ll mid = (l + r) >> 1;
if (pos <= mid) update(ind<<1, pos, v);
else update(ind<<1|1, pos, v);
tr[ind].mx = max(tr[ind<<1].mx, tr[ind<<1|1].mx);
tr[ind].sum = tr[ind<<1].sum + tr[ind<<1|1].sum;
}
void update2(ll ind, ll x, ll y, ll v) {
if (x > y) return;
ll l = tr[ind].l, r = tr[ind].r;
if (x <= l && r <= y) {
tr[ind].tag *= v;
tr[ind].mx *= v;
tr[ind].sum *= v;
return;
}
pushdown(ind);
ll mid = (l + r) >> 1;
if (x <= mid) update2(ind<<1, x, y, v);
if (mid < y) update2(ind<<1|1, x, y, v);
tr[ind].mx = max(tr[ind<<1].mx, tr[ind<<1|1].mx);
tr[ind].sum = tr[ind<<1].sum + tr[ind<<1|1].sum;
}
pii find(ll ind, ll k, ll tp) {
ll l = tr[ind].l, r = tr[ind].r;
if (l == r) {
if (!tp) return make_pair(l, tr[ind].sum - k + 1);
else return make_pair(l, k);
}
pushdown(ind);
if (k <= tr[ind<<1].sum) return find(ind<<1, k, tp);
else return find(ind<<1|1, k - tr[ind<<1].sum, tp);
}
ll query(ll ind, ll x, ll y) {
if (x > y) return 0ll;
ll l = tr[ind].l, r = tr[ind].r;
if (x <= l && r <= y) {
return tr[ind].mx;
}
pushdown(ind);
ll mid = (l + r) >> 1, ret = 0;
if (x <= mid) ret = max(ret, query(ind<<1, x, y));
if (mid < y) ret = max(ret, query(ind<<1|1, x, y));
return ret;
}
int main() {
ttt = read();
for (ll kkk = 1; kkk <= ttt; kkk++) {
printf("Case #%lld:\n", kkk);
n = read(); m = read();
build(1, 1, n);
for (ll i = 1, x, y; i <= m; i++) {
scanf("%s", s); x = read(); y = read();
if (s[0] == 'Q') {
ll ans = 0;
pii l = find(1, x, 0), r = find(1, y, 1);
if (l.first == r.first) {
ans = y - x + 1;
} else {
ans = max(l.second, r.second);
ans = max(ans, query(1, l.first+1, r.first-1));
}
printf("%lld\n", ans);
} else {
pii l = find(1, x, 0), r = find(1, y, 1);
if (l.first == r.first) {
update(1, l.first, y - x + 1);
} else {
update(1, l.first, l.second); update(1, r.first, r.second);
update2(1, l.first + 1, r.first - 1, 2);
}
}
}
}
return 0;
}