题解:P11398 众数
可以说真的是一道不错的题。
我的做法是“分块”。
由于
O((n+m+\sum k)\sqrt n) 做法
会的可以直接跳过。
这其实就是静态区间众数。主要思路是将原序列分成
构建时,考虑到前
修改时,只需要修改后面的块。
注意,修改和构建时维护的所有信息只需要每个块的。
查询时类似,这里只需要查前
这里的
O(\sum k+(n+m)\sqrt n) 做法
在查询的时候,由于右端点是一段区间,显然一个块中的答案可以在块长的时间内求出来。
但是这样仍然会超时。
O(\sum k+(n+m)\log n) 做法
增加块长时修改操作会变快,但是查询中浪费的就会变多。但是又因为查询是最右边一段连续的区间。因此可以将块长设置得不一样,具体地,如果从右往左块长依次为
代码
#include<cstdio>
#include<utility>
#include<algorithm>
#include<math.h>
#define int long long
int t,n,m,b[400005];
int a[400005];
long long d[400005][19];
long long app[400005];
std::pair<long long,int>zhong[400000],ans[400005];
int kuail[19],kuair[19],kuainum=19;
void run(){
long long q;
scanf("%lld",&q);
for(int i=kuainum-1;~i;i--){
ans[kuair[i-1]]=zhong[i-1];
for(int j=kuail[i];j<=kuair[i];j++)app[b[j]]=d[b[j]][i-1];
for(int j=kuail[i];j<=kuair[i];j++){
ans[j]=ans[j-1];
app[b[j]]+=a[j];
if(app[b[j]]>ans[j].first||(app[b[j]]==ans[j].first&&b[j]>ans[j].second))ans[j]={app[b[j]],b[j]};
}
for(int j=kuail[i];j<=kuair[i];j++)app[b[j]]=0;
for(int j=kuair[i];j>=kuail[i];j--){
q^=(1ll*a[j]*ans[j].second);
if(!q){
printf("%lld\n",n-j+1);
return;
}
}
}
}
signed main(){
scanf("%lld%lld%lld",&t,&n,&m);
for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]);
kuainum=log2(n);
for(int i=kuainum-1;~i;i--){
if(kuainum==i+1)kuair[i]=n;
else kuair[i]=kuail[i+1]-1;
if(!i)kuail[i]=1;
else kuail[i]=kuair[i]-(1<<(kuainum-i-1));
}
for(int i=0;i<kuainum;i++){
for(int j=1;j<=n;j++)d[j][i]=d[j][i-1];
for(int j=kuail[i];j<=kuair[i];j++)d[b[j]][i]+=a[j];
zhong[i]=zhong[i-1];
for(int j=kuail[i];j<=kuair[i]&&j<=n;j++)if(d[b[j]][i]>zhong[i].first||(d[b[j]][i]==zhong[i].first&&b[j]>zhong[i].second))zhong[i]={d[b[j]][i],b[j]};
}
while(m--){
int op,x,y;
scanf("%lld",&op);
if(op==2)run();
else{
scanf("%lld%lld",&x,&y);
a[x]+=y;
for(int i=0;i<kuainum;i++){
if(kuair[i]>=x)d[b[x]][i]+=y;
if(d[b[x]][i]>zhong[i].first||(d[b[x]][i]==zhong[i].first&&b[x]>zhong[i].second))zhong[i]={d[b[x]][i],b[x]};
}
}
}
}