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;
}
后记
抄题解是不好的行为哟~