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

浙公网安备 33010602011771号