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

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

题目大意:给一个长度为n,值域为[1, n]的序列{a},要求支持m次操作:

  1. 单点修改
    1 pos val
  2. 询问子区间中某个值域的数的个数,连续的相同数字只记为一个。(即统计数字段的个数)
    2 L R x y

数据范围:

1 ≤ n,m ≤ 2×10^5

1 ≤ a[i] ≤ n


解题思路:

  • 连续重复的数字只记一次。所以考虑将每个数字段除第一个出现外的数字都删去(记为0)。在读入操作的时候暴力模拟,同时维护一个新序列,把原序列的连续重复数字在新序列都记为0。
  • 询问在位置上和值域上都是可作差的。所以将原询问拆分为4个关于(t, pos, val)的子询问
  • 将初值也转换为修改操作
  • 转化成了一个带修改的二维偏序问题;算上时间序就是一个三维偏序

细节:

  • 在修改a[i]时要分别考虑删除原数与添加新数对a[i - 1]和a[i + 1]的影响。删除和添加分开讨论会比较方便
if (pos != n && a[pos] == a[pos + 1])
	A[cnt++] = (ACT) {i, pos + 1, a[pos], 1, 1, 0};
if (pos == 1 || a[pos] != a[pos - 1])
	A[cnt++] = (ACT) {i, pos, a[pos], 1, -1, 0};
a[pos] = val;
if (pos != n && val == a[pos + 1])
	A[cnt++] = (ACT) {i, pos + 1, val, 1, -1, 0};
if (pos == 1 || val != a[pos - 1])
	A[cnt++] = (ACT) {i, pos, val, 1, 1, 0};
  • 要特判每个询问的最左端点,如果是0,且会对答案有影响,(在值域范围内)要给答案+1
ans[Q] = (l == 1 || a[l] != a[l - 1]) ? 0 : 1;
if (a[l] < x || a[l] > y)	ans[Q] = 0; //无需修正
  • 分治时,右半边的修改操作不需要再应用(其对右半边的查询已经被统计,对左半边的查询无影响,)
  • 写分治的题时有几个不错的调试输出点,可以很快地找到错误:
    1. 树状数组的修改、查询函数开头处
    2. 查询操作的统计答案处
    3. 递归头部输出L、R,确定函数调用的状态

CDQ真的快。考场上队友用树套树,手写Treap + 读优还是被卡常,事后CDQ分治连cin都不用改就过了·_·!


AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 2e5 + 1;
int cnt, ans[maxn], Q, a[maxn], n, sym[maxn << 3];
int _v[maxn];
struct ACT {
	int t, pos, val, opt, pls, id;
	bool operator<(ACT &rhs) const {
		return t < rhs.t || (t == rhs.t && \
			(pos < rhs.pos || (pos == rhs.pos && \
			(val < rhs.val || (val == rhs.val && \
			opt < rhs.opt)))));
	}
}A[maxn << 3], T[maxn << 3];

int lowbit(int x) {
	return x & (-x);
}
void add(int x, int v) {
	if (x == 0)
		return;
	//printf("add: %d, %d\n", x, v);
	while (x <= n) {
		_v[x] += v;
		x += lowbit(x);
	}
}
int query(int x) {
	int ret = 0;
	while (x) {
		ret += _v[x];
		x -= lowbit(x);
	}
	return ret;
}
void read() { //(A) {t, pos, val, opt, pls, id}
	int m, i, l, r, x, y, opt, pos, val;
	cin>>n>>m;
	for (i = 0; i < n; ++i) {
		cin>>a[i + 1];
		if (a[i + 1] != a[i])
			A[cnt++] = (ACT) {-1, i + 1, a[i + 1], 1, 1, 0};
	}
	for (i = 0; i < m; ++i) {
		cin>>opt;
		if (opt == 1) {
			cin>>pos>>val;
			if (pos != n && a[pos] == a[pos + 1])
				A[cnt++] = (ACT) {i, pos + 1, a[pos], 1, 1, 0};
			if (pos == 1 || a[pos] != a[pos - 1])
				A[cnt++] = (ACT) {i, pos, a[pos], 1, -1, 0};
			a[pos] = val;
			if (pos != n && val == a[pos + 1])
				A[cnt++] = (ACT) {i, pos + 1, val, 1, -1, 0};
			if (pos == 1 || val != a[pos - 1])
				A[cnt++] = (ACT) {i, pos, val, 1, 1, 0};
		} else {
			cin>>l>>r>>x>>y;
			ans[Q] = (l == 1 || a[l] != a[l - 1]) ? 0 : 1;
			if (a[l] < x || a[l] > y)
				ans[Q] = 0;
			A[cnt++] = (ACT) {i, r, y, 2, 1, Q};
			A[cnt++] = (ACT) {i, r, x - 1, 2, -1, Q};
			A[cnt++] = (ACT) {i, l - 1, y, 2, -1, Q};
			A[cnt++] = (ACT) {i, l - 1, x - 1, 2, 1, Q++};
		}
	}
	sort(A, A + cnt);
}
void mrdge(int l, int r, ACT* T) {
	if (r - l <= 1)	return;
	int mid = l + (r - l) / 2, p = l, q = mid, tot = 0;
	//printf("###l = %d, r = %d\n", l, r);
	mrdge(l, mid, T);	mrdge(mid, r , T);
	//printf("***l = %d, r = %d\n", l, r);
	if (query(n) != 0)
		cout<<"! BIT = "<<query(n)<<endl;
	while (p < mid || q < r) {
		if (q >= r || (p < mid && (A[p].pos < A[q].pos || (A[p].pos == A[q].pos && \
			(A[p].val < A[q].val || (A[p].val == A[q].val && \
			A[p].opt < A[q].opt)))))) {
			if (A[p].opt == 1) {
				add(A[p].val, A[p].pls);
				sym[tot] = 1;
			}
			T[tot++] = A[p++];
		}
		else {
			if (A[q].opt == 2) {
				ans[A[q].id] += query(A[q].val) * A[q].pls;
				//printf("querying[%d]...ans[%d] = %d\n", A[q].val, A[q].id, ans[A[q].id]);
			}
			T[tot++] = A[q++];
		}
	}
	for (int i = 0; i < tot; ++i) {
		if (T[i].opt == 1 && sym[i]) {
			add(T[i].val, -T[i].pls);
			sym[i] = 0;
		}
		A[l + i] = T[i];
	}
	return;
}
void print() {
	for (int i = 0; i < Q; ++i)
		cout<<ans[i]<<endl;
	return;
}
signed main(){
	read();
	mrdge(0, cnt, T);
	print();
	return 0;
}

posted @ 2019-09-13 21:02  BadPlayer  阅读(248)  评论(0编辑  收藏  举报