2019ICPC南昌网络赛 - I. Yukino With Subinterval

题意:

给定一个长为 n 的数组,定义值相同的区间为一子段,有 m 个操作,①:1,pos,v,修改 pos 上的值为 v;②:2,l,r,x,y,询问区间 [ l, r ] 的值域在 [ x, y ] 的段个数,只计最长段。(n, m <= 2e5)

链接:

https://nanti.jisuanke.com/t/41356

题解:

考虑将段转化为点,令 b[i] = a[i] != a[i - 1] ? a[i] : 0,则查询操作转化为求区间 [ l + 1, r ] 的值在 [ x, y ] 的元素个数(l 位置上必然满足为段,单独检查值域即可),修改则维护 b[ pos - 1 ],b[ pos ] 和 b[ pos + 1 ] 即可。显然树状数组套主席树可解(常数巨大),但我又不会 cdq 分治。 那么就愉快分块吧!考虑分块,为了愉快卡常过题,块内套树状数组,则修改操作为 O(logn)。查询涉及整块的块内 logn 查询以及角块暴力,令块大小为 n l o g n \sqrt{nlogn} nlogn ,则整块数目为 n n l o g n \cfrac{n}{\sqrt{nlogn}} nlogn n,整块查询复杂度 O( n l o g n \sqrt{nlogn} nlogn ),角块也是 O( n l o g n \sqrt{nlogn} nlogn ),单次查询复杂度为 O( n l o g n \sqrt{nlogn} nlogn )。然后就卡过去了。

参考代码:
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 2e5 + 5;
const int maxm = 5e2 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int a[maxn], b[maxn];
int li[maxm], ri[maxm], id[maxn];
int c[maxm][maxn];
int n, m;

const int maxs = 1e3 + 5;
char buf[maxs], *p1 = buf, *p2 = buf;
inline char fr(){

	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, maxs, stdin)) == p1 ? -1 : *p1++;
}
#define gc fr()
inline void read(int &x){

	char ch; while(!isdigit(ch = gc)); x = ch ^ 48;
	while(isdigit(ch = gc)) x = x * 10 + (ch ^ 48);
}

#define lowb(x) ((x)&(-x))
void update(int x, int v, int c[]){

	while(x < maxn) c[x] += v, x += lowb(x);
}

int query(int x, int c[]){

	int ret = 0;
	while(x) ret += c[x], x -= lowb(x);
	return ret;
}

void build(){

	int len = sqrt(log(n + 1) * n);
	for(int i = 1; i <= n; ++i) id[i] = (i - 1) / len + 1;
	for(int i = 1; i <= id[n]; ++i) li[i] = (i - 1) * len + 1, ri[i] = i * len; ri[id[n]] = n;
	for(int i = 1; i <= n; ++i) if(b[i]) update(b[i], 1, c[id[i]]);
}

void Update(int x, int v){

	if(v == a[x]) return;
	if(x < n && v == a[x + 1]) update(b[x + 1], -1, c[id[x + 1]]), b[x + 1] = 0;
	if(x > 1 && v == a[x - 1] && b[x]) update(b[x], -1, c[id[x]]), b[x] = 0;
	else{

		if(b[x]) update(b[x], -1, c[id[x]]);
		b[x] = v, update(b[x], 1, c[id[x]]);
	}
	a[x] = v;
}

int Query(int l, int r, int x, int y){

	if(l > r) return 0;
	int p1 = id[l], p2 = id[r], ret = 0;
	if(p1 == p2){

		for(int i = l; i <= r; ++i) ret += b[i] >= x && b[i] <= y;
		return ret;
	}
	for(int i = p1 + 1; i < p2; ++i) ret += query(y, c[i]) - query(x - 1, c[i]);
	for(int i = l; i <= ri[p1]; ++i) ret += b[i] >= x && b[i] <= y;
	for(int i = li[p2]; i <= r; ++i) ret += b[i] >= x && b[i] <= y;
	return ret;
}

int main(){

	read(n), read(m);
	for(int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i] != a[i - 1] ? a[i] : 0;
	build();
	while(m--){

		int opt, l, r, x, y; read(opt), read(l), read(r);
		if(opt == 1) Update(l, r);
		else{

			read(x), read(y);
			int ret = Query(l + 1, r, x, y) + (a[l] >= x && a[l] <= y);
			printf("%d\n", ret);
		}
	}
}

 
cdq 分治待补。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值