【ICPC】2022合肥热身赛 C. Funds Management | 树状数组

179 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

【ICPC】2022合肥热身赛 C. Funds Management | 树状数组

题目

image.png

题目大意

对于 nn 个在 1 到 1000 之间的正整数 a1,a2,a3,...,ana_1,a_2,a_3,...,a_n,有 qq 个操作,每个操作可能是以下的一种:

  1. Query:对于给定的 llrr,输出 al,al+1,...,ara_l,a_{l+1},...,a_r 的中位数。具体的,输出将 al,al+1,...,ara_l,a_{l+1},...,a_r 从小到大进行排序后的 al+r2+1a_{\lfloor\frac{l+r}{2}\rfloor+1}
  2. Modify:对于给定的 llrr,令 al=al+ra_l=a_l+r。如果修改后 ala_l 的值小于 1 或 大于 1000,本次操作无效。且输入的 rr 只有可能是 1 或者 -1。

对于每个操作 1,输出相应的答案。

思路

据说我的思路好像不太正确而且赛中算的时间复杂度也有点 TLE 但是依然通过了此题……于是还是写了题解。

思路是开 1000 个树状数组,s[i][x]s[i][x] 表示前 ii 个位置里 数字 xx 出现的次数是 s[i][x]s[i][x]。首先我们先把每个数字本身加入到树状数组中,对于每个操作:

  • 如果是操作 1,就统计前 rr 个位置每个数字出现的次数和前 l1l-1 个位置里每个数字出现的次数,二者作差,自小到达统计小于等于 jj 的数字的数量,第一次不小于 rl+12+1\lfloor\frac{r-l+1}{2}\rfloor+1 时,当前下标即为答案。
  • 对于操作 2,我们判断操作是否有效。对于有效操作只需要先减少 ala_l 出现的次数,再增加 al+ra_l+r 出现的次数即可。

代码

#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;
		
}