CF1039E Summer Oenothera Exhibition

CF1039E Summer Oenothera Exhibition

一脸不可做的亚子
时限7s,一看就不是polylog的题
首先令 k i = w − k i k_i=w-k_i ki=wki,然后 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写错调了半条,还是要更熟练一些才行啊

posted @ 2020-11-26 21:42  lahlah  阅读(29)  评论(0编辑  收藏  举报