LGP11398 众数 学习笔记
LGP11398 众数 学习笔记
前言
虽然题目名字叫众数,但复杂度却是 \(\text{polylog}\) 的。
而且,甚至……
本题涉及的主要知识点:
【3】递推法
【4】倍增法
题意简述
你有 \(n\) 个可重集。第 \(i\) 个可重集可以表示为 \((a_i,b_i)\),表示其由 \(a_i\) 个 \(b_i\) 组成。
定义下标 \(i\in [1,n]\) 的权值 \(c_i\) 为前 \(i\) 个可重集的并的众数元素(若有多个众数,取值最大者)乘以 \(a_i\)。
有 \(m\) 个操作:
1 x y将 \(a_x\) 增加 \(y\)。保证 \(y\) 非负。2 q询问最小的正整数 \(k\) 满足 \(\oplus_{i=n-k+1}^{n} c_i=q\)。
保证操作二有解,且 \(\sum k\le 5\times 10^7\)。
\(n,m\le 3\times 10^5\),\(1\le b_i\le n\)。保证对于任意时刻的 \(a_i\) 都有 \(1\le a_i\le 10^9\)。
做法解析
首先暴力是 \(O(nm)\) 级别的,做法显然,略。
顺着 \(b_i=1\)、\(b_i\le 2\) 这两档部分分,有一种思路是你在处理询问,从后往前扫的时候实时求当前前缀的众数。因为我们知道前缀的众数等于全局的减掉后缀的,所以你通过一个支持单点减小全局求最大值的数据结构就可以每次维护这个东西。显然可以用线段树、平衡树状物解决此问题,时间复杂度在 \((n+\sum k)\log V\) 水平,其中 \(V\) 为 \(\sum b_i\) 的值域。
但是这东西过不了,因为 \(\sum k=5\times 10^7\)。
顺着 \(k\le 300\) 这档部分分可以得到正解。你发现 \(k\) 很小,这意味着前 \(n-300\) 个元素必定参与计算。而前 \(n-300\) 个元素内部那些可重集的顺序我们就不关心了。所以我们直接维护一个对于前 \(n-300\) 个元素的计数器就行了。每次询问直接让 \(k\) 从 \(300\) 推到 \(1\)。时间复杂度 \(m\max k\)。
然后你发现正解就出来了,我们维护若干形如前 \(n-2^i\) 个元素的计数器。对于每个询问,显然肯定有一款适合。你从 \(i\) 最小的计数器开始推,时间复杂度是对的。因为对于 \(2^i\le k\le 2^{i+1}\),\(\sum_{j=0}^{i+1}2^i\le 4k\)。
反正就是这样了,接下来就是代码了。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=3e5+5,MaxNb=20;
int Tpn,N,M,A[MaxN],B[MaxN],Opt,X,Y;
vector<int> stt;lolo cnt[MaxNb][MaxN],mde[MaxNb],val[MaxN];
bool check(int k,int b,int m){return cnt[k][b]>cnt[k][m]||(cnt[k][b]==cnt[k][m]&&b>m);}
void modify(int x,int y){
A[x]+=y;
for(int j=0;j<stt.size();j++){
if(stt[j]<x)break;
cnt[j][B[x]]+=y;if(check(j,B[x],mde[j]))mde[j]=B[x];
}
}
int query(int q){
int cend=N,ans=0,xsum=0;
for(int j=0;j<stt.size();j++){
int cmde=mde[j];
for(int i=stt[j]+1;i<=cend;i++){
cnt[j][B[i]]+=A[i];
if(check(j,B[i],cmde))cmde=B[i];
val[i]=cmde*A[i];
}
for(int i=cend;i>stt[j];i--){
xsum^=val[i];
if(xsum==q&&!ans)ans=i;
cnt[j][B[i]]-=A[i];
}
if(ans>0)return N+1-ans;
cend=stt[j];
}
return 0;
}
int main(){
readis(Tpn,N,M);
for(int j=1;N-j>0;j<<=1)stt.push_back(N-j);
stt.push_back(0);
for(int i=1;i<=N;i++)readis(A[i],B[i]);
for(int j=0;j<=stt.size();j++){
for(int i=1;i<=stt[j];i++){
cnt[j][B[i]]+=A[i];
if(check(j,B[i],mde[j]))mde[j]=B[i];
}
}
for(int i=1;i<=M;i++){
readis(Opt,X);
if(Opt==1)readi(Y),modify(X,Y);
if(Opt==2)writil(query(X));
}
return 0;
}
浙公网安备 33010602011771号