A
B

题解:P8930 「TERRA-OI R1」神,不惧死亡

P8930 「TERRA-OI R1」神,不惧死亡

大水紫

首先发现是神秘区间问题,考虑支持区间操作的数据结构。

发现数据范围是 \(1e5\) ,而且维护的东西很神秘,直接考虑分块或莫队。

如果你不了解分块

将题意转化为寻找在区间 \([l,r]\) 中,值处于 \([a,b]\) 中第一个出现次数为偶数的点(设为 \(x\))的后继(第一个比它大的数)。

这很好理解,因为处于 \(x\) 之前的数肯定是出现过奇数次或者没出现。

我们将 \(x\) 进行操作至其全部删除,则比他小的数的出现次数减一,也即奇数次变成偶数次。

然后继续这个过程。

这样就能把小于等于 \(x\) 的数全部删除,则最小值即为 \(x\) 的后继(第一个比 \(x\) 大的数)。

显然先删除比 \(x\) 小的数一定没有直接删 \(x\) 优。

如果删除比 \(x\) 大的数,则在删到 \(x\) 的时候会有两种情况:

  • 一种是 \(x\) 是奇数个,则无法删除 \(x\),最小值即为 \(x\),劣于 \(x\) 的后继。

  • 一种是 \(x\) 是偶数个,则因为比 \(x\) 小的数的奇偶性与其不同,则会造成无法删除比其小的数,最小值小于 \(x\),劣于 \(x\) 的后继。

因此得证,可以如此转化题意。

因为是与值域有关,所以考虑值域分块。

我们要维护两个查询,一个是查询第一个出现偶数次的值,另一个是每个值的后继。

我们依然采用传统的散块暴力查。

对于每个整块,维护两个数组,一个存该块内出现偶数次的数的个数,一个维护块内出现不同的的数的个数。

然后对于区间操作,直接上莫队即可。

时间复杂度 \(O(n ^ \frac{5}{3})\)

可以通过此题(顺便拿到了次优解)。

另外提一嘴,貌似可以在线做,直接对区间分块,维护每个数的出现次数前缀和,然后上一棵树状数组维护加减操作。时间复杂度 \(O(m \sqrt[3]{\frac{n^2}{m}log^2{n}})\),大概是 \(O(n \sqrt{n})\),乱胡的,不对别打我 qwq。

标程

#include<bits/stdc++.h>
#define int long long 
#define con putchar_unlocked(' ')
#define ent putchar_unlocked('\n')
#define Blue_Archive return 0
using namespace std;
constexpr int N = 1e5 + 3;
constexpr int M = 320;
constexpr int mid = 6e4 + 7;
constexpr int INF = INT32_MAX;
constexpr char me[] = "終末なにしてますか?忙しいですか?救ってもらっていいですか?";

int n;
int m;
int len1;
int len2;
int cntq;
int cntc;
int a[N];
int st[M];
int ed[M];
int id1[N];
int id2[N];
int cnt[N];
int ans[N];
int cntk[M]; // 每个块存在偶数的个数
int totk[M]; // 每个块存在的数的个数

struct miku
{
	int l,r,a,b;
	int id,tim;
}q[N];

struct mika
{
	int pos;
	int val;
}chg[N];

inline int read()
{
	int k = 0,f = 1;
	char c = getchar_unlocked();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar_unlocked();
	}
	while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar_unlocked();
	return k * f;
}

inline void write(int x)
{
	if(x < 0) putchar_unlocked('-'),x = -x;
	if(x > 9) write(x / 10);
	putchar_unlocked(x % 10 + '0');
}

inline void add(int x)
{
	if(!cnt[x]) totk[id2[x]] ++;
	cnt[x] ++;
	if(cnt[x] && cnt[x] % 2 == 0) cntk[id2[x]] ++;
}

inline void del(int x)
{
	if(cnt[x] && cnt[x] % 2 == 0) cntk[id2[x]] --;
	cnt[x] --;
	if(!cnt[x]) totk[id2[x]] --;
}

inline int query_nxt(int val,int r) // 查找后继
{
	if(id2[val] == id2[r])
	{
		for(int i = val + 1;i <= r;i ++) if(cnt[i]) return i;
		return -1;
	}
	for(int i = val + 1;i <= ed[id2[val]];i ++) if(cnt[i]) return i;
	for(int i = id2[val] + 1;i < id2[r];i ++)
	{
		if(!totk[i]) continue;
		for(int j = st[i];j <= ed[i];j ++) if(cnt[j]) return j;
	}
	for(int i = st[id2[r]];i <= r;i ++) if(cnt[i]) return i;
	return -1;
}

inline int query(int l,int r) // 查找第一个为偶数的数
{
	if(id2[l] == id2[r])
	{
		for(int i = l;i <= r;i ++)
		{
			if(cnt[i] && cnt[i] % 2 == 0) return query_nxt(i,r);
		}
		return -1;
	}
	for(int i = l;i <= ed[id2[l]];i ++) if(cnt[i] && cnt[i] % 2 == 0) return query_nxt(i,r);
	for(int i = id2[l] + 1;i < id2[r];i ++)
	{
		if(!cntk[i]) continue;
		for(int j = st[i];j <= ed[i];j ++)
		{
			if(cnt[j] && cnt[j] % 2 == 0) return query_nxt(j,r);
		}
	} 
	for(int i = st[id2[r]];i <= r;i ++) if(cnt[i] && cnt[i] % 2 == 0) return query_nxt(i,r);
	return -1;
}

signed main()
{
	// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
	n = read();
	m = read();
	len1 = pow(n,0.667);
	for(int i = 1;i <= n;i ++) 
	{
		a[i] = read();
		id1[i] = (i - 1) / len1 + 1;
	}
	len2 = sqrt(n);
	for(int i = 1;i <= n;i ++)
	{
		id2[i] = (i - 1) / len2 + 1;
		if(!st[id2[i]]) st[id2[i]] = i;
		ed[id2[i]] = i;
	}
	cntc = 1;
	for(int i = 1,op;i <= m;i ++)
	{
		op = read();
		if(op == 1)
		{
			chg[++ cntc].pos = read();
			chg[cntc].val = read();
		}
		if(op == 2)
		{
			q[++ cntq].l = read();
			q[cntq].r = read();
			q[cntq].a = read();
			q[cntq].b = read();
			q[cntq].tim = cntc;
			q[cntq].id = cntq;
		}
	}
	sort(q + 1,q + cntq + 1,[](miku a,miku b){return id1[a.l] == id1[b.l] ? id1[a.r] == id1[b.r] ? a.tim < b.tim : a.r < b.r : a.l < b.l;});
	for(int i = 1,l = 1,r = 0,t = 1;i <= cntq;i ++)
	{
		while(l < q[i].l) del(a[l ++]);
		while(l > q[i].l) add(a[-- l]);
		while(r < q[i].r) add(a[++ r]);
		while(r > q[i].r) del(a[r --]);
		while(t > q[i].tim)
		{
			if(chg[t].pos >= l && chg[t].pos <= r) del(a[chg[t].pos]);
			a[chg[t].pos] -= chg[t].val;
			if(chg[t].pos >= l && chg[t].pos <= r) add(a[chg[t].pos]);
			t --;
		}
		while(t < q[i].tim)
		{
			t ++;
			if(chg[t].pos >= l && chg[t].pos <= r) del(a[chg[t].pos]);
			a[chg[t].pos] += chg[t].val;
			if(chg[t].pos >= l && chg[t].pos <= r) add(a[chg[t].pos]);
		}
		ans[q[i].id] = query(q[i].a,q[i].b);
	}
	for(int i = 1;i <= cntq;i ++) write(ans[i]),ent;
	Blue_Archive;
}

后记

抄题解是不好的行为哟~

posted @ 2025-10-24 17:55  MyShiroko  阅读(16)  评论(0)    收藏  举报