2019 南昌网络赛 I. Yukino With Subinterval(CDQ 分治)

在这里插入图片描述


题目大意:有一个数组,支持单点修改:将第 i 个位置的值变成 v。让你查询[L,R]区间内,值域在[x,y] 范围内,连续的段数,连续的段数指值相同位置连续的一段,并且连续段的长度在[L,R]内必须尽可能的长。


先对数组预处理,将连续的段缩到左端点,即新建一个数组 b, 若 a [ i ] ! = a [ i − 1 ] a[i] != a[i - 1] a[i]!=a[i1],则 b [ i ] = 1 b[i] = 1 b[i]=1
题目转变为:查询 [ L + 1 , R ] [L + 1,R] [L+1,R],值域在 [ x , y ] [x,y] [x,y]内 1 的个数。(由于 L位置 必有贡献,因此直接查询 [ L + 1 , R ] [L + 1,R] [L+1,R]的贡献即可,对于L位置判断一下是否在值域范围内)

这个问题是一个二维偏序问题,但要支持修改,引入时间维度变成一个三维问题,可以用CDQ分治解决。

如何支持修改:细想一下修改一个位置的值除了影响自己这个位置的值外,只会影响后面一个位置的值,或者由0变成1,或者由1变成0。
对这两个位置的值枚举两种转变情况,然后转变为更新操作即可。

(CDQ分治做得少,思维对不上)
小结:CDQ分治的通常做法是将所有操作都变成更新操作和修改操作,这对这个操作序列按时间维度(或三维偏序的第一维)进行分治。


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
#define lowbit(i) (i & (-i))
struct node{
	int x,y,v,op,id;
	node(int xi = 0,int yi = 0,int vi = 0,int pi = 0,int di = 0) {
		x = xi; y = yi; op = pi; id = di;
		v = vi;
	}
	bool operator < (const node & rhs) const {
		return id < rhs.id;
	}
}qes[maxn],tmp[maxn];
int cnt,n,m,a[maxn];
int val[maxn],ans[maxn];
void upd(int x,int v) {
	for(; x < maxn; x += lowbit(x)) 
		val[x] += v;
}
int qry(int x) {
	int ans = 0;
	for(; x; x -= lowbit(x))
		ans += val[x];
	return ans;
}
void cdq(int l,int r) {
	if(l == r) return ;
	int mid = l + r >> 1;
	cdq(l,mid);cdq(mid + 1,r);
	int top = 0;
	for(int i = l,j = mid + 1; i <= mid || j <= r;) {
		if(i <= mid && (j > r || qes[i].x <= qes[j].x)) {
			if(qes[i].op == 1) upd(qes[i].y,qes[i].v);
			tmp[++top] = qes[i++];
		} else {
			if(qes[j].op == 2) {
				ans[qes[j].id] += qry(qes[j].y);
			}
			if(qes[j].op == 3) {
				ans[qes[j].id] -= qry(qes[j].y);
			}
			tmp[++top] = qes[j++];
		}
	}
	for(int i = l; i <= mid; i++)
		if(qes[i].op == 1) upd(qes[i].y,-qes[i].v);
	for(int i = l; i <= r; i++)
		qes[i] = tmp[i - l + 1];
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++) {
		scanf("%d",&a[i]);
		if(a[i] != a[i - 1])
			qes[++cnt] = node(i,a[i],1,1,0);
	}
	int tot = 0;
	for(int i = 1; i <= m; i++) {
		int op,x1,y1,x2,y2;
		scanf("%d",&op);
		if(op == 1) {
			scanf("%d%d",&x1,&y1);
			if(a[x1] == y1) continue;
			if(a[x1] != a[x1 - 1]) 
				qes[++cnt] = node(x1,a[x1],-1,1,0);
			if(x1 < n && a[x1 + 1] != a[x1]) 
				qes[++cnt] = node(x1 + 1,a[x1 + 1],-1,1,0);
			a[x1] = y1;
			if(a[x1] != a[x1 - 1]) 
				qes[++cnt] = node(x1,a[x1],1,1,0);
			if(x1 < n && a[x1 + 1] != a[x1]) 
				qes[++cnt] = node(x1 + 1,a[x1 + 1],1,1,0);
		} else {
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			tot++;
			if(a[x1] <= y2 && a[x1] >= x2) ans[tot]++;
			qes[++cnt] = node(x1,x2 - 1,0,2,tot);
			qes[++cnt] = node(y1,y2,0,2,tot);
			qes[++cnt] = node(x1,y2,0,3,tot);
			qes[++cnt] = node(y1,x2 - 1,0,3,tot);
		}
	}
	cdq(1,cnt);
	for(int i = 1; i <= tot; i++)
		printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值