D 筱玛爱线段树 (二次差分+负数取模)
=》关于求余和取模的区别以及负数取摸
a%b =》 ((a%b)+b)%b;
同余模公式
这里 1000000004 就是相当于-3
因为(1000000004+3)%mod=0
链接:https://ac.nowcoder.com/acm/contest/946/D
来源:牛客网
题目描述
筱玛是一个热爱线段树的好筱玛。
筱玛的爷爷马爷在游戏中被筱玛吊打了,于是他恼羞成怒,决定给筱玛出这样一道数据结构题:
给定一个长度为nn的数组AA,刚开始每一项的值均为00。
支持以下两种操作,操作共mm次:
1 l r1 l r:将Al∼ArAl∼Ar的每一项的值加上11。
2 l r2 l r:执行操作编号在[l,r][l,r]内的所有操作各一次,保证rr小于当前操作的编号。
mm次操作结束后,你要告诉马爷AA数组变成什么样子了。
由于答案可能会很大,你只需要输出数组AA中的每个数在模109+7109+7意义下的值。
输入描述:
第一行两个数n,mn,m,分别表示数组长度及操作次数。 接下来mm行,每行三个数opt,l,ropt,l,r,表示一次操作。
输出描述:
输出一行共nn个数,表示mm次操作结束后,A1∼AnA1∼An的值。
示例1
输入
复制
4 3 1 1 3 2 1 1 1 1 3
输出
复制
3 3 3 0
备注:
对于100%的数据,1≤n≤105,1≤m≤1051≤n≤105,1≤m≤105。
题意:求多次区间修改操作后,每个数的值
思路:从后往前n~1,用一个数组dadd[]统计每个操作命令执行的次数,此处利用差分前缀和统计。另一个数组add[]统计每一个差分区间变化值,最后一次对add[]前缀和求每个数的值。
#include<bits/stdc++.h>
using namespace std;
int n,m;
#define mod 1000000007
long long a[100005],b[100005];
struct ex{
int num,l,r;
}q[100005];
long long int add[100005],dadd[100005];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&q[i].num,&q[i].l,&q[i].r);
}
dadd[m+1]=1;
for(int i=m;i>=1;i--)
{
dadd[i]+=dadd[i+1];
if(q[i].num==1)
{
add[q[i].l]+=dadd[i]%mod;
add[q[i].r+1]=((add[q[i].r+1]-dadd[i]%mod)%mod+mod)%mod;
//!!!!!!!!负数取模 a%b=((a%b)+b)%b
add[q[i].l]%=mod;
add[q[i].r]%=mod;
}
else if(q[i].num==2)
{
dadd[q[i].r]+=dadd[i]%mod;
dadd[q[i].l-1]=((dadd[q[i].l-1]-dadd[i])%mod+mod)%mod;
//!!!!!!!!负数取模 a%b=((a%b)+b)%b
dadd[q[i].r]%=mod;
}
}
long long int sum=0;
for(int i=1;i<=n;i++)
{
sum+=add[i];
sum%=mod;
printf("%d%s",sum,i==n?"\n":" ");
}
}
线段树:
#include<bits/stdc++.h> using namespace std; long long int a[100005*4],b[4*100005]; int n,m; #define mod 1000000007 int ex1[100005],ex2[100005],ex0[100005]; long long ans[100005]; void update(int o,int l,int r,int ql,int qr,long long q) { if(ql<=l&&r<=qr) { b[o]=((b[o]+q)%mod+mod)%mod;//b[o]懒惰标记 return ; } int mm=(l+r)/2; if(ql<=mm) update(o*2,l,mm,ql,qr,q); if(mm<qr) update(o*2+1,mm+1,r,ql,qr,q); return ; } long long getnum(int o,int l,int r,int k)//求k操作上的标记数,即该操作运行次数 { if(l==r) return b[o]; int m=(l+r)/2; long long res=b[o]; if(k<=m) res+=getnum(o*2,l,m,k); else res+=getnum(o*2+1,m+1,r,k); return res%mod;//此区间上的所有标记相加 即总运行次数 } int main() { cin>>n>>m; for(int i=1;i<=m;i++) { scanf("%d%d%d",&ex0[i],&ex1[i],&ex2[i]); } b[1]=1;//初始化所有操作运行一遍 for(int i=m;i>=1;i--) { long long x=getnum(1,1,n,i); if(ex0[i]==2) { update(1,1,n,ex1[i],ex2[i],x);//2 l r [l,r]所有操作运行次数+x //此线段树用于处理操作运行次数 } else { ans[ex1[i]]=((ans[ex1[i]]+x)%mod+mod)%mod; ans[ex2[i]+1]=((ans[ex2[i]+1]-x)%mod+mod)%mod; } } long long int sum=0; for(int i=1;i<=n;i++) { sum=(sum+ans[i])%mod;//差分 printf("%lld%c",sum,i==n?'\n':' '); } } //9 8 //1 1 3 //1 4 5 //1 2 6 //2 2 3 //1 4 6 //1 2 3 //2 2 4 //1 9 9