CF1039E Summer Oenothera Exhibition
CF1039E Summer Oenothera Exhibition
一脸不可做的亚子
时限7s,一看就不是polylog的题
首先令
k
i
=
w
−
k
i
k_i=w-k_i
ki=w−ki,然后
w
w
w就没用了
考虑对一个
k
k
k怎么做
可以很容易的发现直接贪心就可以了
对于每个区间每次尽量往右取,能取就取
于是得到了一个
O
(
n
q
)
O(nq)
O(nq)的做法
询问可以离线
于是把它们从小到大排序
像弹飞绵羊那样 记
n
x
t
[
i
]
nxt[i]
nxt[i]为以
i
i
i为左端点,右端点最有能延伸到哪里
然后就转换为求从
1
1
1跳到
n
+
1
n+1
n+1的次数
这个用LCT维护就好了
然而发现每次都要重新更新一下
n
x
t
nxt
nxt …
O
(
n
2
l
o
g
n
)
O(n^2logn)
O(n2logn)连暴力都跑不过
考虑根号分治
对于长度
n
x
t
[
i
]
−
i
<
=
s
q
r
t
(
n
)
nxt[i]-i<=sqrt(n)
nxt[i]−i<=sqrt(n)的直接用LCT维护
对于长度
n
x
t
[
i
]
−
i
>
s
q
r
t
(
n
)
nxt[i]-i>sqrt(n)
nxt[i]−i>sqrt(n)的暴力跳,最多跳
s
q
r
t
(
n
)
sqrt(n)
sqrt(n)次
然后就行了
时间复杂度
O
(
n
n
log
n
)
O(n\sqrt n \ \log n)
O(nn logn)
关于区间极差最好用
s
t
st
st表,会快一些
具体实现看代码吧
code:
#include<bits/stdc++.h>
#define N 400050
#define INF 1e9
using namespace std;
int ch[N][2], size[N], rev[N], fa[N];
void update(int x) {
size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void pushr(int x) {
swap(ch[x][0], ch[x][1]), rev[x] ^= 1;
}
void pushdown(int x) {
if(rev[x]) {
if(ch[x][0]) pushr(ch[x][0]);
if(ch[x][1]) pushr(ch[x][1]);
rev[x] = 0;
}
}
int nroot(int x) {
return ch[fa[x]][0] == x || ch[fa[x]][1] == x;
}
int get(int x) {
return ch[fa[x]][1] == x;
}
void rotate(int x) {
int f = fa[x], gf = fa[f], k = get(x);
if(nroot(f)) ch[gf][get(f)] = x; fa[x] = gf;
if(ch[x][!k]) fa[ch[x][!k]] = f; ch[f][k] = ch[x][!k];
ch[x][!k] = f, fa[f] = x;
update(f), update(x);
}
int sta[N];
void splay(int x) {
int now = x, top = 0;
sta[++ top] = now;
while(nroot(now)) sta[++ top] = now = fa[now];
while(top) pushdown(sta[top --]);
while(nroot(x)) {
int f = fa[x];
if(nroot(f))
rotate(get(x) == get(f)? f : x);
rotate(x);
}
update(x);
}
void access(int x) {
for(int y = 0; x; x = fa[y = x])
splay(x), ch[x][1] = y, pushdown(x);
}
int getroot(int x) {
access(x), splay(x);
for(; ch[x][0];) pushdown(x), x = ch[x][0];
splay(x);
return x;
}
void link(int x, int y) {
access(x); //注意这里不能换根,因为是往后跳
fa[x] = y;
}
void cut(int x, int y) {
access(x), splay(y); // 同上
fa[x] = ch[y][1] = 0;
update(y);
}
//-------------------------------LCT 板子--------------------------
int logg[N], mi[N][22], ma[N][22], n, w, qq, a[N], ls[N], ANS[N], nxt[N], vis[N];
vector<int> ha[N];
int getmi(int l, int r) {
int k = logg[r - l + 1];
return min(mi[l][k], mi[r - (1 << k) + 1][k]);
}
int getma(int l, int r) {
int k = logg[r - l + 1];
return max(ma[l][k], ma[r - (1 << k) + 1][k]);
}
int calc(int l, int r) {//st表求极差
return getma(l, r) - getmi(l, r);
}
struct Q {
int v, id;
} q[N];
int cmp(Q x, Q y) {
return x.v < y.v;
}
int gnxt(int x, int k) { // 大于sqrt(n)的 暴力往后跳
int l = x, r = n + 1;
while(l + 1 < r) {
int mid = (l + r) >> 1;
if(calc(x, mid) > k) r = mid;
else l = mid;
}
return r;
}
int main() {
scanf("%d%d%d", &n, &w, &qq);
int blo = sqrt(n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++) mi[i][0] = ma[i][0] = a[i], size[i] = 1;
logg[1] = 0;
for(int i = 2; i <= n; i ++) logg[i] = logg[i >> 1] + 1;
mi[n + 1][0] = - INF, ma[n + 1][0] = INF;
for(int j = 1; j <= 17; j ++)
for(int i = 1; i <= n + 1; i ++)
mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]),
ma[i][j] = max(ma[i][j - 1], ma[i + (1 << (j - 1))][j - 1]);//st表预处理
for(int i = 1; i <= qq; i ++) {
scanf("%d", &q[i].v); q[i].v = w - q[i].v; //把 k 转换一手
q[i].id = i;
}
sort(q + 1, q + 1 + qq, cmp);
for(int i = 1; i <= qq; i ++) ls[i] = q[i].v;
for(int i = 1; i <= n; i ++)
nxt[i] = i, ha[1].push_back(i); // 先把全部的左端点丢到第一个询问那里
for(int i = 1; i <= qq; i ++) {
for(int ii = 0; ii < ha[i].size(); ii ++) {
int pos = ha[i][ii], j = nxt[pos] + 1;
cut(pos, nxt[pos]);// 左端点为pos的这条边可以往后延伸,先断开和原来连的
while(j <= n && j - pos <= blo && calc(pos, j) <= q[i].v) j ++;//往后延伸
if(j - pos > blo)//如果 >sqrt(n)就标记一下,暴力跳
vis[pos] = 1;
else { //否则就更新一下nxt,LCT上连边,丢到下一个可能会让它右端点延伸的询问那里
nxt[pos] = j;
link(pos, nxt[pos]);
int jj = lower_bound(ls + 1, ls + 1 + qq, calc(pos, nxt[pos])) - ls;
ha[jj].push_back(pos);
}
}
int gs = 0;
for(int j = 1; j <= n; j = gnxt(j, q[i].v)) {//大力跳
if(!vis[j])//如果边长 <= sqrt(n) 就在LCT上找ROOT, 然后答案加上这条链的长度
access(j), splay(j), gs += size[j] - 1, j = getroot(j);
if(j > n) break;
gs ++;
}
ANS[q[i].id] = gs - 1;//更新答案
}
for(int i = 1; i <= qq; i ++) printf("%d\n", ANS[i]);
return 0;
}
LCT写错调了半条,还是要更熟练一些才行啊