洛谷P3984-数据结构 题解
题面
这题精,真的精
前言:不要被题目背景和描述误导!
Solution:
题目大意
给定一段序列,请你做到区间修改和区间询问。
区间询问即 在 \(L\) 到 \(R\) 区间内,乘上下标后取模的值在条件范围内的元素的个数 。
暴力方法
时间不充裕者跳过
一道题目, 正解都是由简单到困难一步一步推导出来的 。我们不如先使用暴力来做。
暴力为了优化一点,我们不妨用 差分 进行区间修改的优化
差分是什么?
比如一次需要你对 \(X\) 到 \(Y\) 区间内的所有数加上 \(K\) ,正常都是for(int i : x~y)a[i]++,之间复杂度明显为 N。
而差分,只需要另外那一个数组,在 \(X\) 位置加上 \(K\) , 在 \(Y + 1\) 位置减去 \(K\) ,到最后询问时累加前缀和即可。
差分数组定义: 差分数组 [ i ] 即 原始数组 [ i ] - 原始数组 [ i - 1 ]
差分模板:
#include
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<
回归正题
修改操作使用差分进行优化,而查询操作就需要动态暴力枚举完成了。
暴力查询操作代码:
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<= Min && shit <= Max) num++;
}
return num;
}
我们再认真读一遍题,会发现,前面的 Q 次操作是动态的(由于数组会修改),所以我们
不得不暴力查询。但是后面那只“非三次元仓鼠”的询问时静态的(数组不会变了)。
所以:
我们只要预处理前 i 位有多少个符合要求的元素即可!
正解思路总结:
首先对于前面 Q 次操作,使用差分进行修改,暴力进行查询。后面 \(F\) 次静态查询通过预处理后 O(1) 复杂度即可求出答案。 注意:该题对于long long的要求极其之严格,需要大家注意!
AC代码:
#include//潇洒的万能头
#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<= 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<= Min && shit <= Max) a[i]++;//合法则累加
a[i] += a[i - 1];//预处理前缀和
}
while(F--){//循环
int x = read(),y = read();//读入左边界和右边界
cout<