【2020五校联考NOIP #6】三格缩进

题意:
给出 \(n\) 个数 \(a_1,a_2,\dots,a_n\),你要进行 \(m\) 次操作,每次操作有两种类型:
\(1\ p\ x\):将 \(a_p\) 改为 \(x\)
\(2\ y\):求 \(\sum\limits_{i=1}^ny/a_i+y\%a_i\)
\(1 \leq n,m \leq 10^5,1 \leq a_i \leq 2 \times 10^5\)

一道水水的 T2。
考虑整除分块,不同的 \(y/a_i\) 最多只有 \(2\sqrt{200000}\) 种,并且对于所有 \(y/x=z\)\(x\) 一定可以构成一个区间 \([l,r]\)
这部分对答案的贡献就是 \(\sum\limits_{a_i \in [l,r]}z+y-a_i \times z\)
设有 \(cnt\)\(a_i \in [l,r]\),它们的和为 \(sum\)
上述式子可以变为 \((z+y) \times cnt-z \times sum\)
接下来我们的任务就是求出 \(cnt,sum\)

相信一名正常人第一反应应该是树状数组,维护值为 \(x\)\(a_i\) 出现了多少次。
稍加分析就可以得出时间复杂度,查询 \(m\sqrt{200000}\) 次,修改 \(m\) 次,总时间复杂度 \(\mathcal O(n\sqrt{n}\log n)\)(假设 \(n,m\) 同阶)。
出题人比较良心,放树状数组过去了。
可是作为一名 OIer,我们不能满足于现状,需要探寻更加高效的求解方法,而这,也是这道题的重头戏所在。
回顾树状数组的时间复杂度计算,发现瓶颈在询问上,要进行 \(m\sqrt{200000}\) 查询,而修改只用进行 \(m\) 次。
可我们树状数组查询和修改复杂度都是 \(\log n\) 的。修改复杂度仅仅只有 \(m \log n\)
这启发我们使用查询复杂度 \(\mathcal O(1)\),而修改复杂度 \(\mathcal O(\sqrt{n})\) 的数据结构。
我们考虑分块。维护整块的前缀和和块内前缀和,这样查询复杂度就可以做到 \(\mathcal O(1)\) 了,而修改复杂度 \(\mathcal O(\sqrt{n})\)
本题实现非常简单,但思想比较重要。

/*
Contest: -
Problem: NFLSOJ 707
Author: tzc_wk
Time: 2020.10.15
*/
#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define pb			push_back
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a)		a.begin(),a.end()
#define fill0(a)	memset(a,0,sizeof(a))
#define fill1(a)	memset(a,-1,sizeof(a))
#define fillbig(a)	memset(a,0x3f,sizeof(a))
#define y1			y1010101010101
#define y0			y0101010101010
typedef pair<int,int> pii;
typedef long long ll;
int n,q,a[100005];
const int BLOCK_SZ=447;
int L[453],R[453],bel[200005],blk;
struct blockstruct{
	ll sum[453],blk_sum[453][453];
	inline void add(int x,int v){
		int t=bel[x];
		for(int i=x-L[t]+1;i<=R[t]-L[t]+1;i++)
			blk_sum[t][i]+=v;
		for(int i=t;i<=blk;i++) sum[i]+=v;
	}
	inline ll query(int l,int r){
		int _l=bel[l],_r=bel[r];
		if(_l==_r) return blk_sum[_l][r-L[_l]+1]-blk_sum[_l][l-L[_l]];
		else return blk_sum[_l][R[_l]-L[_l]+1]-blk_sum[_l][l-L[_l]]+sum[_r-1]-sum[_l]+blk_sum[_r][r-L[_r]+1];
	}
} b1,b2;
int main(){
	scanf("%d%d",&n,&q);
	blk=448;for(int i=1;i<=blk;i++){
		L[i]=(i-1)*BLOCK_SZ+1;
		R[i]=min(i*BLOCK_SZ,200000);
		for(int j=L[i];j<=R[i];j++) bel[j]=i;
	}
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) b1.add(a[i],1),b2.add(a[i],a[i]);
	while(q--){
		int opt;scanf("%d",&opt);
		if(opt==1){
			int p,x;scanf("%d%d",&p,&x);
			b1.add(a[p],-1);b2.add(a[p],-a[p]);
			a[p]=x;
			b1.add(a[p],1);b2.add(a[p],a[p]);
		} else {
			int y;scanf("%d",&y);ll s=0;
			for(int l=1,r;l<=y;l=r+1){
				r=y/(y/l);
				s+=b1.query(l,r)*(y/l)+y*b1.query(l,r)-b2.query(l,r)*(y/l);
//				printf("%d %d %d %d\n",l,r,b1.query(l,r),b2.query(l,r));
			}
			s+=y*b1.query(y+1,200000);
			printf("%lld\n",s);
		}
	}
	return 0;
}
posted @ 2020-10-17 16:53  tzc_wk  阅读(206)  评论(0编辑  收藏  举报