Description
两个长度均为n 的序列a 和b,其中b 是一个 1 到 n 的排列,a 一开始为全0 序列 现在有两种操作: 1 l r 表示a 中[l,r] 的数全部加1 2 l r 询问 [l,r]中的sum(a[i]/b[i]);这里sum表示求和;即i<=r;i>=l;每一个a[i]/b[i]的结果向下取整,然后再相加! 请你写一个程序能执行两个操作
Input
第一行两个正整数n,m 表示序列长度和操作数 接下来一行n 个整数表示bi; 接下来m 行每行包括每次操作的操作类型与操作区间;
(n,m<=1e5)
Output
对于每一种2 操作输出询问答案
Sample Input
5 5 1 3 2 4 5 1 1 3 2 1 5 1 2 4 1 2 3 2 2 4
Sample Output
1 2
思路:
如果按照单点修改,时间复杂度是o(m*nlongn)会超时。在每个节点,我们可以记录在当前节点下区间最少需要多少次1操作会使sum变化(记为mi)。因为每次只加一,所以不会出现上一次当前区间mi>0,这次mi<0.每次操作1时,当mi==0,说明需要改变sum,进行单点修改。遇到二输出即可。
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
const int N = 1e5 + 10;
#define ls (o<<1)
#define rs (o<<1|1)
struct data
{
int mi;//当前区间改变所需要1操作数
int su;//和
}sum[N << 2];
long long a[N];
int lazy[N << 2];
int n, m, b[N];
void pushupsum(int o)
{
sum[o].su = sum[ls].su + sum[rs].su;
}
void pushupmi(int o)
{
sum[o].mi = min(sum[ls].mi, sum[rs].mi);
}
void build(int o, int l, int r)//建树
{
if (l == r)
{
sum[o].mi = b[l];
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushupmi(o);
}
void pushdown(int o, int l, int r)//下传lazy标记
{
if (lazy[o])
{
int mid = (l + r) >> 1;
sum[ls].mi -= lazy[o];
sum[rs].mi -= lazy[o];
lazy[ls] += lazy[o];
lazy[rs] += lazy[o];
lazy[o] = 0;
return;
}
}
void modify(int o, int l, int r)//单点修改
{
if (sum[o].mi) return;
if (l == r)
{
sum[o].su++;
sum[o].mi = b[l];
return;
}
pushdown(o, l, r);
int mid = (l + r) / 2;
modify(ls, l, mid);
modify(rs, mid + 1, r);
pushupsum(o);
pushupmi(o);
}
void change(int o, int l, int r, int ql, int qr)//区间修改
{
if (ql <= l && qr >= r)
{
sum[o].mi--;
lazy[o]++;
modify(o, l, r);
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1;
if (ql <= mid)
change(ls, l, mid, ql, qr);
if (qr > mid)
change(rs, mid + 1, r, ql, qr);
pushupsum(o);
pushupmi(o);
}
long long ask(int o, int l, int r, int ql, int qr)//求区间维护值
{
if (ql <= l && qr >= r)
{
return sum[o].su;
}
pushdown(o, l, r);
long long ans = 0;
int mid = (l + r) >> 1;
if (ql <= mid)
ans += ask(ls, l, mid, ql, qr);
if (qr > mid)
ans += ask(rs, mid + 1, r, ql, qr);
return ans;
}
int main()
{
scanf("%d%d", &n, &m);
memset(lazy, 0, sizeof lazy);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
build(1, 1, n);
for (int i = 1; i <= m; i++)
{
int k, ql, qr;
scanf("%d", &k);
if (k == 1)
{
scanf("%d%d", &ql, &qr);
change(1, 1, n, ql, qr);
}
else
{
scanf("%d%d", &ql, &qr);
printf("%lld\n", ask(1, 1, n, ql, qr));
}
}
return 0;
}