zoukankan      html  css  js  c++  java
  • 【2020五校联考NOIP #6】三格缩进

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

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

    相信一名正常人第一反应应该是树状数组,维护值为 (x)(a_i) 出现了多少次。
    稍加分析就可以得出时间复杂度,查询 (msqrt{200000}) 次,修改 (m) 次,总时间复杂度 (mathcal O(nsqrt{n}log n))(假设 (n,m) 同阶)。
    出题人比较良心,放树状数组过去了。
    可是作为一名 OIer,我们不能满足于现状,需要探寻更加高效的求解方法,而这,也是这道题的重头戏所在。
    回顾树状数组的时间复杂度计算,发现瓶颈在询问上,要进行 (msqrt{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
    ",l,r,b1.query(l,r),b2.query(l,r));
    			}
    			s+=y*b1.query(y+1,200000);
    			printf("%lld
    ",s);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    【转】算法的时间复杂度
    FFT 物理意义(转)
    【转】FIR学习1
    【转】DFT DTFT DFS FFT的关系
    【转】TCL中的数组
    【转】setup time和hold time的周期问题(slack)
    【转】TCL语法简介
    【转】亚稳态分析
    ubuntu下Samba服务器搭建
    第一次生成uImage出现的问题解决
  • 原文地址:https://www.cnblogs.com/ET2006/p/13831752.html
Copyright © 2011-2022 走看看