本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
【ICPC】2022合肥热身赛 C. Funds Management | 树状数组
题目
题目大意
对于 个在 1 到 1000 之间的正整数 ,有 个操作,每个操作可能是以下的一种:
- Query:对于给定的 和 ,输出 的中位数。具体的,输出将 从小到大进行排序后的 。
- Modify:对于给定的 和 ,令 。如果修改后 的值小于 1 或 大于 1000,本次操作无效。且输入的 只有可能是 1 或者 -1。
对于每个操作 1,输出相应的答案。
思路
据说我的思路好像不太正确而且赛中算的时间复杂度也有点 TLE 但是依然通过了此题……于是还是写了题解。
思路是开 1000 个树状数组, 表示前 个位置里 数字 出现的次数是 。首先我们先把每个数字本身加入到树状数组中,对于每个操作:
- 如果是操作 1,就统计前 个位置每个数字出现的次数和前 个位置里每个数字出现的次数,二者作差,自小到达统计小于等于 的数字的数量,第一次不小于 时,当前下标即为答案。
- 对于操作 2,我们判断操作是否有效。对于有效操作只需要先减少 出现的次数,再增加 出现的次数即可。
代码
#include <stdio.h>
#include <string.h>
#define lowbit(x) (x&-x)
int s[10001][1001],q,n,a[10001];
int cntl[1001],cntr[1001];
void modify(int t,int x,int d)
{
for (;t<=n;t+=lowbit(t))
s[t][x]+=d;
}
void query(int t,int cntt[])
{
for (;t>=1;t-=lowbit(t))
{
for (int i=1;i<=1000;++i)
cntt[i]+=s[t][i];
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
modify(i,a[i],1);
}
scanf("%d",&q);
int opt,l,r,cnt,id,ans;
while (q--)
{
scanf("%d%d%d",&opt,&l,&r);
if (opt==1)
{
cnt=0;
query(r,cntr);
query(l-1,cntl);
id=(r-l+1)/2+1;
ans=0;
for (int i=1;i<=1000;++i)
{
cnt+=cntr[i]-cntl[i];
if (cnt>=id&&!ans) ans=i;
cntl[i]=cntr[i]=0;
}
printf("%d\n",ans);
}
else if (a[l]+r>=1&&a[l]+r<=1000)
{
modify(l,a[l]+r,1);
modify(l,a[l],-1);
a[l]+=r;
}
}
return 0;
}