1 条题解
-
0
自动搬运
来自洛谷,原作者为

wimg6_
1+1=?搬运于
2025-08-24 23:00:08,当前版本为作者最后更新于2024-06-29 20:14:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
因为要使每个小朋友拿到的糖种类尽量多,所以我们可以让一个小朋友取所有有剩余的糖各一颗,然后剩下的糖的总数就是答案。
那么对于第 种糖,答案就是 ,其中 表示目前还有多少颗第 种糖,但是我们会发现这个方向很难继续优化。
于是我们转换思路,记录 表示有 颗糖的糖果种类的数量,那么答案就是 ,同时注意到 和 都可以用线段树在 时间内得出。
故对于前两个操作就是线段树单点修改,最后一个操作就是线段树区间查询,复杂度是 。
值得注意的是,正常数组需要开 ,因为有可能会出现非常多的一号操作。线段树记得开四倍。
#include<bits/stdc++.h> using namespace std; #define int long long const long long N=2e6; int n,q,k,a[N+10],f[N+10],s[N+10],tree[4*N+10],t[4*N+10]; int read(){ int x=0;char ac=getchar(); while(ac<'0' || ac>'9') ac=getchar(); while(ac>='0' && ac<='9') x=x*10+ac-'0',ac=getchar(); return x; } void push_up(int id){ tree[id]=tree[id*2]+tree[id*2+1]; t[id]=t[id*2]+t[id*2+1]; } void build_tree(int id,int l,int r){ if(l==r){ tree[id]=s[l]*l,t[id]=s[l]; return ; } int mid=(l+r)/2; build_tree(id*2,l,mid); build_tree(id*2+1,mid+1,r); push_up(id); } void change(int id,int l,int r,int x){ if(l==r){ tree[id]=s[l]*l,t[id]=s[l]; return ; } int mid=(l+r)/2; if(x<=mid) change(id*2,l,mid,x); else change(id*2+1,mid+1,r,x); push_up(id); } int cal(int id,int l,int r,int x,int y){ if(x<=l && r<=y) return tree[id]-k*t[id]; int mid=(l+r)/2,ans=0; if(x<=mid) ans+=cal(id*2,l,mid,x,y); if(y>mid) ans+=cal(id*2+1,mid+1,r,x,y); return ans; } signed main(){ n=read(),q=read(); for(int i=1;i<=n;i++) a[i]=read(),f[a[i]]++; for(int i=1;i<=n;i++) s[f[i]]++; //for(int i=1;i<=n;i++) // printf("%d ",s[i]); build_tree(1,1,N); while(q--){ int op=read(); if(op==1){ int x=read(); s[f[x]]--,change(1,1,N,f[x]),f[x]++,s[f[x]]++,change(1,1,N,f[x]); } else if(op==2){ int x=read(); s[f[x]]--,change(1,1,N,f[x]),f[x]--,s[f[x]]++,change(1,1,N,f[x]); } else if(op==3){ k=read(); //printf("%d %d %d ",tree[1],t[1],cal(1,1,n,1,k)); printf("%lld\n",cal(1,1,N,k+1,N)); } } return 0; }
- 1
信息
- ID
- 10427
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者