题目描述
Zxyer是M国一个伟大的膜法师,现在他制作出了n个膜法球,每个膜法球有一个初始膜力值,由亍zxyer现在在修炼膜法,因此zxyer现在会对这些按照编号为1~n顺序依次排列的膜法球进行m次操作,每次zxyer会将一个区间的膜法球拿出,然后将他们的膜力值依次增加一个等差数列,现在zxyer想要知道在他施法全部结束后,这些膜法球各自的膜力值。
输入
第一行2个整数,n,m,定义参见问题描述。
第2行n个整数,ai,表示膜法球各自的初始膜力值。
第3~m+2行,每行3个整数,Li,Ri,qi,si表示第i次操作区间的左右端点(对应着等差数列的首项与末项),等差数列的公差,首项。
输出
输出n行,每行1个整数,表示施法结束后每个膜法球的膜力值。
样例输入
5 4 1 2 3 4 5 1 3 2 1 4 5 -1 2 1 5 -3 10 2 4 6 -5
样例输出
12 7 13 14 4
提示
对于100%的数据,1≤n,m≤200000
来源
20170416 NOIP模拟赛 by zxyer
这是学长出的一道题。
暴力的做法就只能拿部分分,其实我们大多数人也只有部分分。
线段树的做法就O(nlogn)就可以过了,不过还有更快的O(n+m)的方法。
简单的说就是两次前缀和。
因为题意要求在区间内加上一个差分序列,告诉公差。
我们假设公差是1,那么在前缀和数组a上修改就是a[l]到a[r]加每一位都加1。
因此可以用线段树做。
然而没有多次查询,我们就可以再来一个数组a的前缀和数组b,在b[l]上加1,在b[r]上减1。就可以实现a[l]到a[r]加每一位都加1。
然后就大功告成了。
注意,会爆int。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const long long maxn=200010; long long read(){ long long t=1,num=0; char c=getchar(); while(c>‘9‘||c<‘0‘){if(c==‘-‘)t=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){num=num*10+c-‘0‘;c=getchar();} return num*t; } long long a[maxn],b[maxn]={0},c[maxn]={0},ans[maxn]={0}; long long n,m,l,r,q,s; int main() { n=read();m=read(); for(long long i=1;i<=n;i++)a[i]=read(); for(long long i=1;i<=m;i++){ l=read();r=read();q=read();s=read(); c[l+1]+=q;c[r+1]-=q; long long g=(r-l)*q; c[l]+=s;c[l+1]-=s; c[r+1]-=g+s;c[r+2]+=g+s; } for(long long i=1;i<=n;i++)b[i]=b[i-1]+c[i]; for(long long i=1;i<=n;i++)ans[i]=ans[i-1]+b[i]; for(long long i=1;i<=n;i++)cout<<a[i]+ans[i]<<"\n"; return 0; }
本文由Yzyet编写,网址为www.cnblogs.com/Yzyet。非Yzyet同意,禁止转载,侵权者必究。
时间: 2024-10-13 19:12:33