题目描述
自从看了《哈利波特》,小Y就十分渴望获得魔法值。于是他和一群向往魔法的孩子(当然这些孩子们都是不会魔法的)来到了哈利波特的家,大家坐成一排。哈利波特会不时的给大家传输魔法。
哈利每次会选择一个区间,给这个区间里的孩子们传输魔法:最左边的孩子给一点,第二个给两点……哈利有时会突然问你某一个孩子已经有了多少魔法。
输入格式
第一行两个正整数 N,M,表示有 N 个孩子,哈利有 M 次操作。
接下来 M 行,每行代表一个操作。第一个字符为 ci,若 ci=‘C’则此次操作为传送魔法操作,接下来会有两个整数Li,Ri,表示此次送魔法值的区间。若 ci=‘Q’则此次操作为询问操作,接下来一个整数xi,表示询问第xi个孩子当前的魔法值。
输出格式
对于每组询问输出一行,仅包含一个整数,表示答案对 1,000,000,007 取模(mod)的结果。
样例
INPUT
3 4
C 1 3
Q 2
C 2 3
Q 2
OUTPUT
2
3
数据规模
对于 30%的数据,N,M≤1,000;
对于 100%的数据,N,M≤100,000。
一句话题意
要求维护一个序列a,满足以下两个操作
一、查询i位置的数值a_i%mod
二、给定一个区间[l,r],l<=i<=r,让a_i += (i - l + 1)
一句话题解
线段树+差分
题解
怎么哪里都有差分
首先考虑用线段树维护数组a,但是发现对于"C"操作无论是懒标记还是区间维护都不好写,想到等差数列的差分数组每一项都是公差,正好符合线段树的区间维护,于是用线段树维护数组a的差分数组,然后对于操作"Q"只需要求sum(1,x)即可,而操作"C"[l,r]则是将[l,r]之间都加1,而在r+1处加上-(r-l+1)即可
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();}
return x * f;
}
int n, m;
const int maxn = 1e5 + 10, mod = 1e9 + 7;
class Segment_tree{
private:
struct node{
int sum, lazytag;int l, r;
} d[maxn * 4];
void build(int l,int r,int p){
if(l == r){d[p].sum = d[p].lazytag = 0;d[p].l = d[p].r =l;return;}
int mid = (l + r) >> 1;
build(l,mid,p << 1);build(mid + 1,r,(p << 1) | 1);
d[p].l = l;d[p].r = r;
return;
}
node mergenode(node a,node b,int p){node tmp;
tmp.sum = a.sum + b.sum;
tmp.lazytag = d[p].lazytag;
tmp.l=d[p].l;tmp.r=d[p].r;
return tmp;
}
void pushdown(int l,int r,int p){
int mid = (l + r) >> 1;
if(d[p].lazytag){
d[p << 1 ].lazytag += d[p].lazytag;
d[(p << 1) | 1].lazytag += d[p].lazytag;
d[p << 1 ].sum += d[p].lazytag * (mid - l + 1);
d[p << 1 ].sum %= mod;
d[(p << 1) | 1].sum += d[p].lazytag * (r - mid);
d[(p << 1) | 1].sum %= mod;
d[p].lazytag = 0;
}
return;
}
void update(int l,int r,int s,int t,int p,int change){
if(l <= s && t <= r){
d[p].sum += change * (t - s + 1);
d[p].lazytag += change;
d[p].sum %= mod;
return;
}
int mid = (s + t) >> 1;
pushdown(s,t,p);
if(l <= mid)update(l,r,s,mid,p << 1,change);
if(mid < r)update(l,r,mid + 1,t,(p << 1) | 1,change);
d[p] = mergenode(d[p << 1],d[(p << 1) | 1],p);
}
int query(int l,int r,int s,int t,int p){
if(l <= s && t <= r)return d[p].sum;
int mid = (s + t) >> 1;
pushdown(s,t,p);
int res = 0;if(l <= mid) res = query(l,r,s,mid,p << 1) % mod;
if(mid < r) res += query(l,r,mid + 1,t,(p << 1) | 1);return res % mod;
}
public:
void DEBUG(){
// puts("DEBUG");
// for(int i = 1;i <= n * 4;i++){
// printf("[ %lld , %lld ]:d[%lld].sum = %lld,d[%lld].laz = %lld\n",d[i].l,d[i].r,i,d[i].sum,i,d[i].lazytag);
// }
}
void update(int x,int y){update(x,y,1,n + 1,1,1);update(y + 1,y + 1,1,n + 1,1,-y + x - 1);}
int query(int x){return query(1,x,1,n + 1,1) % mod;}
void build(int n){build(1,n + 1,1);}
}tree;
signed main(){
n = read(); m = read();
tree.build(n);
char ch;int x, y;
for(int i = 1;i <= m;i++){
ch = getchar();
while(ch == ' ' || ch == '\n')ch = getchar();
if(ch == 'C'){
x = read(); y = read();
tree.update(x,y);
}
else{
x = read();
printf("%lld\n",tree.query(x));
}
// tree.DEBUG();
}
return 0;
}
总结
对于线段树的问题,需要考虑的是如何合并,如何下传懒标记和node的定义