• 洛谷 P2801 教主的魔法


    题目链接

    话说分块的坑点好多啊,一不小心就会越界什么的真是麻烦QWQ

    0x00 思路

    面对区间修改区间查询,我们有两种基本思路:线段树和分块

    可是很明显我们查询的东西不满足区间加法的性质,而且每次查询的标准在变,我们考虑直接上分块暴力维护

    0x01 分块思路

    定义块大小为(size = sqrt(n))

    查询/修改的区间为([l,r]),查询/修改的值为(v)

    (l)所在的块为(lb),(r)所在的块为(rb)

    查询:

    1. 对于完整的块,我们将其中的元素排序,二分查找最小的符合条件的数的位置,作差即可计算贡献

    2. 对于不完整的块,我们直接枚举每一个数,计算贡献

    修改:

    1. 对于完整的块,我们用(block)数组打标记,表示整个块全部加上(v),就像线段树的懒标记一样,查找的时候刚好可以用上

    2. 对于不完整的块,我们直接枚举每个数,加上(v),然后重新将块内的元素排序

    细节:

    1. 我们开两个数组,(a)(tmp),(a)数组存储的是原数组,(tmp)存储的是排序后的数组,排序我们是每次只排序一块内的数

    2. 每次要重排序块内的数时,可以从(a)中复制一份到(tmp)中,然后排序

    3. 各种下标要注意,时刻保证块的右端点不能超过(n)

    4. 对于(lb = rb)的情况,要特判

    0x02 Code

    
    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000010
    #define M 1010
    int read(){
    	int x=0; char c=getchar(); int flag=1;
    	while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
    	while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
    	return x*flag;
    }
    int n,m,size,belong[N];
    long long a[N],tmp[N],block[M];
    signed main(){
        n = read(),m = read(),size = sqrt(n);
        for(int i = 1;i <= n;i ++) a[i] = read(),belong[i] = (i - 1) / size + 1,tmp[i] = a[i];
        for(int i = 1;(i - 1) * size + 1 <= n;i ++){
    	    int l = (i - 1) * size + 1,r = min(n,i * size);
    	    sort(tmp + l,tmp + r + 1);
    	}
        while(m --){
    	    char opt;
    	    int l,r,v;
    	    scanf(" %c %d %d %d",&opt,&l,&r,&v);
    	    if(opt == 'A'){
    		    int lb = belong[l],rb = belong[r],ans = 0;
    		    if(lb == rb){
    			    for(int i = l;i <= r;i ++) if(a[i] + block[lb] >= v) ++ ans;
    			    printf("%d
    ",ans);
    			    continue;
    			}
    		    for(int i = lb + 1;i <= rb - 1;i ++){
    			    int l = (i - 1) * size + 1,r = i * size,ps = r + 1;
    			    while(l <= r){
    				    int mid = ((l + r) >> 1);
    				    if(tmp[mid] + block[i] >= v) { ps = mid; r = mid - 1; }
    				    else l = mid + 1;
    				}
    				ans += i * size - ps + 1;
    			}
    			for(int i = l;i <= lb * size;i ++) if(a[i] + block[lb] >= v) ++ ans;
    			for(int i = (rb - 1) * size + 1;i <= r;i ++) if(a[i] + block[rb] >= v) ++ ans;
    			printf("%d
    ",ans); 
    		}
    		if(opt == 'M'){
    		    int lb = belong[l],rb = belong[r];
    		    if(lb == rb){
    			    for(int i = l;i <= r;i ++) a[i] += v;
    			    for(int i = (lb - 1) * size + 1;i <= min(lb * size,n);i ++) tmp[i] = a[i];
    			    sort(tmp + (lb - 1) * size + 1,tmp + min(lb * size,n) + 1);
    			    continue;
    			}
    		    for (int i = lb + 1;i <= rb - 1;i ++) block[i] += v;
    		    
    			for (int i = l;i <= lb * size;i ++) a[i] += v;
    		    for (int i = (lb - 1) * size + 1;i <= lb * size;i ++) tmp[i] = a[i];
    		    sort(tmp + (lb - 1) * size + 1,tmp + lb * size + 1);
    		    
    			for (int i = (rb - 1) * size + 1;i <= r;i ++) a[i] += v;
    		    for (int i = (rb - 1) * size + 1;i <= min(rb * size,n);i ++) tmp[i] = a[i];
    		    sort(tmp + (rb - 1) * size + 1,tmp + min(rb * size,n) + 1);
    		}
    	}
        return 0;
    }
    
    
    
  • 相关阅读:
    JS文本框下拉提示效果
    JS动态添加删除表格行
    JS验证 数字 电话号码 传真 邮箱 手机号码 邮编 日期
    TreeView 中CheckBox级联选中问题
    HashTable Dictionary
    JS操作Frame对象
    Winfrom 中怎样在回车时设置焦点
    Word 操作(未完待续)
    HTML5特性——prefetching预加载功能
    10个实用的 jQuery Mobile 插件推荐
  • 原文地址:https://www.cnblogs.com/zzhzzh123/p/12240038.html
Copyright © 2020-2023  润新知