【2020五校联考NOIP #6】三格缩进
题意:
给出 n 个数 a1,a2,…,an,你要进行 m 次操作,每次操作有两种类型:
1 p x:将 ap 改为 x。
2 y:求 n∑i=1y/ai+y%ai。
1≤n,m≤105,1≤ai≤2×105。
一道水水的 T2。
考虑整除分块,不同的 y/ai 最多只有 2√200000 种,并且对于所有 y/x=z 的 x 一定可以构成一个区间 [l,r]。
这部分对答案的贡献就是 ∑ai∈[l,r]z+y−ai×z。
设有 cnt 个 ai∈[l,r],它们的和为 sum。
上述式子可以变为 (z+y)×cnt−z×sum。
接下来我们的任务就是求出 cnt,sum。
相信一名正常人第一反应应该是树状数组,维护值为 x 的 ai 出现了多少次。
稍加分析就可以得出时间复杂度,查询 m√200000 次,修改 m 次,总时间复杂度 O(n√nlogn)(假设 n,m 同阶)。
出题人比较良心,放树状数组过去了。
可是作为一名 OIer,我们不能满足于现状,需要探寻更加高效的求解方法,而这,也是这道题的重头戏所在。
回顾树状数组的时间复杂度计算,发现瓶颈在询问上,要进行 m√200000 查询,而修改只用进行 m 次。
可我们树状数组查询和修改复杂度都是 logn 的。修改复杂度仅仅只有 mlogn。
这启发我们使用查询复杂度 O(1),而修改复杂度 O(√n) 的数据结构。
我们考虑分块。维护整块的前缀和和块内前缀和,这样查询复杂度就可以做到 O(1) 了,而修改复杂度 O(√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; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步