• Evanyou Blog 彩带


    题面

    这题精,真的精

    前言:不要被题目背景和描述误导!


    Solution:

    题目大意

    给定一段序列,请你做到区间修改和区间询问。

    区间询问即 (L)(R) 区间内,乘上下标后取模的值在条件范围内的元素的个数

    暴力方法

    时间不充裕者跳过

    一道题目, 正解都是由简单到困难一步一步推导出来的 。我们不如先使用暴力来做。

    暴力为了优化一点,我们不妨用 差分 进行区间修改的优化

    差分是什么?

    比如一次需要你对 (X)(Y) 区间内的所有数加上 (K) ,正常都是for(int i : x~y)a[i]++,之间复杂度明显为 N。

    而差分,只需要另外那一个数组,在 (X) 位置加上 (K) , 在 (Y + 1) 位置减去 (K) ,到最后询问时累加前缀和即可。

    差分数组定义: 差分数组 [ i ] 即 原始数组 [ i ] - 原始数组 [ i - 1 ]

    差分模板:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const int MAXN = 10000 + 5;//定义常量
    
    int a[MAXN];//原始序列
    int b[MAXN];//差分序列 
    
    inline int read(){//快速读入 
        int f = 1, x = 0;
        char c = getchar();
    
        while (c < '0' || c > '9')
        {
            if (c == '-')
                f = -1;
            c = getchar();
        }
    
        while (c >= '0' && c <= '9')
        {
            x = x * 10 + c - '0';
            c = getchar();
        }
    
        return f * x;
    }
    
    int main(){
    	int n = read();//输入元素个数
    	for(int i = 1;i <= n; i += 1){//+= 1会比 ++ 更快(某机房巨佬说的) 
    		a[i] = read();
    		b[i] = a[i] - a[i - 1];//初始化差分数组 
    	} 
    	
    	int m = read();//输入操作次数
    	while(m--){
    		int x = read(),y = read(),z = read();
    		//左边界       右边界     修改数量
    		b[x] += z,b[y + 1] -= z;//差分操作 
    	} 
    	
    	for(int i = 1;i <= n; i++){
    		b[i] += b[i - 1];//累加差分数组(前缀和)
    		cout<<b[i]<<" ";//输出 
    	}
    	
    	return 0;
    }
    

    回归正题

    修改操作使用差分进行优化,而查询操作就需要动态暴力枚举完成了。

    暴力查询操作代码:

    int query(int x,int y){
    	int num = 0;
    	ll B[MAXN];//临时用来累加差分数组的数组
    	memset(B,0,sizeof(B));
    	for(int i = 1;i <= n + 1; i++){
    		B[i] = b[i] + B[i - 1];
    		//cout<<B[i]<<" "; 
    	}
    	//cout<<"
    ";
    	for(int i = x;i <= y; i++){
    		ll shit = MOD(B[i] * i);//取模操作需要单独处理负数情况,这里我单独用了一个函数
    		//cout<<i<<" "<<shit<<"
    ";
    		if(shit >= Min && shit <= Max) num++;
    	}
    	return num;
    }
    

    我们再认真读一遍题,会发现,前面的 Q 次操作是动态的(由于数组会修改),所以我们

    不得不暴力查询。但是后面那只“非三次元仓鼠”的询问时静态的(数组不会变了)。

    所以:

    我们只要预处理前 i 位有多少个符合要求的元素即可!

    正解思路总结:

    首先对于前面 Q 次操作,使用差分进行修改,暴力进行查询。后面 (F) 次静态查询通过预处理后 O(1) 复杂度即可求出答案。 注意:该题对于long long的要求极其之严格,需要大家注意!

    AC代码:

    #include<bits/stdc++.h>//潇洒的万能头 
    #define ll long long//懒人必备 
    using namespace std;
    
    const int MAXN = 80000 + 5;//定义常量 
    
    int n,q,Min,Max;//如题所述,Q为操作次数 
    ll mod;//mod的值 
    ll b[MAXN];//差分数组 
    ll a[MAXN];//预处理数组 
    
    inline ll read(){//快速读入 
        ll f = 1, x = 0;
        char c = getchar();
    
        while (c < '0' || c > '9')
        {
            if (c == '-')
                f = -1;
            c = getchar();
        }
    
        while (c >= '0' && c <= '9')
        {
            x = x * 10 + c - '0';
            c = getchar();
        }
    
        return f * x;
    }
    
    ll MOD(ll xxx){//单独处理负数取模 
    	if(xxx > 0)return xxx % mod;
    	return -1 * ((-xxx) % mod);
    }
    
    int query(int x,int y){//暴力询问 
    	int num = 0;//累加器 
    	ll B[MAXN];//临时用来累加差分数组的数组
    	memset(B,0,sizeof(B));
    	for(int i = 1;i <= n + 1; i++){
    		B[i] = b[i] + B[i - 1];//累加前缀和 
    		//cout<<B[i]<<" "; 
    	}
    	//cout<<"
    ";
    	for(int i = x;i <= y; i++){
    		ll shit = MOD(B[i] * i);
    		//cout<<i<<" "<<shit<<"
    ";
    		if(shit >= Min && shit <= Max) num++;//合法则累加 
    	}
    	return num;
    }
    
    int main(){//开始了 
    //	freopen("dis.out","w",stdout);
    	n = read(),q = read(),mod = read(),Min = read(),Max = read();//快速读入 
    	for(int i = 1;i <= q; i++){//循环操作次数 
    		char c;
    		cin>>c;//读入操作 
    		if(c == 'A'){//如果是区间修改 
    			int x = read(),y = read();//读入左边界和右边界 
    			ll z = read();//读入修改的值 
    			b[x] += z,b[y + 1] -= z;//差分数组 O(1) 修改 
    		}
    		if(c == 'Q'){//如果是询问 
    			int x = read(),y = read();//入左边界和右边界 
    			cout<<query(x,y)<<"
    ";//输出答案 
    		}
    	}
    	ll F = read();//“非三次元仓鼠”静态操作 
    	for(int i = 1;i <= n + 1; i++){ 
    		b[i] += b[i - 1];//先累加差分的前缀和 
    	}
    	for(int i = 1;i <= n; i++){
    		ll shit = MOD(b[i] * i);
    		if(shit >= Min && shit <= Max) a[i]++;//合法则累加 
    		a[i] += a[i - 1];//预处理前缀和 
    	}
    	while(F--){//循环 
    		int x = read(),y = read();//读入左边界和右边界 
    		cout<<a[y] - a[x - 1]<<"
    ";//输出 
    	}
    	return 0;//end
    }
    
  • 相关阅读:
    jdbc概述
    MongoDB(三):数据库操作、集合操作
    MongoDB(二):在Windows环境安装MongoDB
    MongoDB(一):NoSQL简介、MongoDB简介
    python基础(36):pymysql模块
    Web前端基础(19):jQuery基础(六)
    Web前端基础(18):jQuery基础(五)
    Web前端基础(17):jQuery基础(四)
    Web前端基础(16):jQuery基础(三)
    Web前端基础(15):jQuery基础(二)
  • 原文地址:https://www.cnblogs.com/CJYBlog/p/shu-ju-jie-gou.html
Copyright © 2020-2023  润新知