现在您有一个序列 ,求 。
我们有 的暴力算法,但它并不够完美。我们可以做到 。
具体的做法就是定义一个前缀和数组 ,其中 ,那么根据加法的性质,我们可以有 。
同时,我们可以用 的时间复杂度计算 ,根据定义,我们有 。以上算法就是前缀和。
当然,前缀和算法并不是只能运用于加法,事实上,很多运算都可以用前缀和算法加速。比如我们常见的异或运算(因为一个数异或两次同一个数相当于没有异或)。可以说,有逆运算的运算都可以用前缀和加速(乘法除外)。
差分是前缀和的逆运算。所谓差分,就是定义一个差分数组 ,其中, 。
差分的第一个基本运算就是利用差分数组还原原数组,这可以用前缀和算法解决。即 。
差分的第二个基本运算是
解决区间修改。比如把
都加上一个数
。因为区间同时加上同一个数,所以其内部元素的相对差值并不会改变(即
不变)。事实上,改变的只有
,我们只需
修改它们即可(D[l]+=T,d[r+1]-=T
)。
洛谷P3909
【题意】: 对于
,求
的值。
【思路】: 原式
,然后记
,
,原式
,我们就可以在
的时间复杂度内计算出答案。
【代码】:
#define ll long long
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
const int N=1e6+1e3;
ll s[N],t[N],a[N],ans[N],n;
const ll mod=1e9+7;
int main(){
freopen("t1.in","r",stdin);
n=read();//ans=0;
for(int i=1;i<=n;i++){
a[i]=read();
s[i]=(s[i-1]+a[i])%mod;
}
// s[i]=a[1]+a[2]+a[3]+a[4]+..+a[i]
for(int i=1;i<=n;i++)
t[i]=(t[i-1]+a[i]*s[i-1]%mod)%mod;
// t[i]=a[2]*s[1]+a[3]*s[2]+a[4]*a[3]+...+a[i]*s[i-1]
for(int i=1;i<=n;i++)
ans[i]=(ans[i-1]+a[i]*t[i-1]%mod)%mod;
// ans[i]=a[3]*t[2]+a[4]*t[3]+a[5]*t[4]+...+a[i]*t[i-1]
cout<<(ans[n]*6)%mod;
return 0;
}
洛谷P3819
【题意】: 涞坊路
是一条长
米的道路,道路上的坐标范围从
到
,路上有
座房子,第
座房子建在坐标为
的地方,其中住了
人。
松江1843路公交车
要在这条路上建一个公交站,市政府希望让最多的人得到方便,因此希望所有的每一个的居民,从家到车站的距离的总和最短。
公交站应该建在哪里呢?
【思路】: 记 表示在第 个单位坐标建公交车时的距离总和,那么 函数是一个单谷函数,我们可以用三分法求出其最小值。
考虑如何在 的时间复杂度内求出 。
把数轴分为两部分: 和 。记 在第 号和第 号居民区之间( 可以用二分法求出)。 。
我们可以继续优化,记 。则 。
在这里,笔者采用了定量三分法,循环三分 次。
时间复杂度:
【代码】:
#define ll long long
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
char c=0;ll x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
const int N=1e5+1e3;
ll ss[N],sr[N],ans,n,x[N];
inline ll F(ll t){
int i=upper_bound(x+1,x+n+1,t)-x-1;
// 当前计算的坐标位于第i个居民区和第i+1个居民区之间
register ll cnt=0ll;//cnt:所有居民的总贡献
cnt+=t*sr[i]-ss[i];//从第1号到第i号居民的总贡献
cnt+=(ss[n]-ss[i])-t*(sr[n]-sr[i]);//从第i+1号到第n号居民的总贡献
return cnt;//现在的cnt即所有居民的总贡献
}
ll l,r,lmid,rmid;
int main(){
freopen("t1.in","r",stdin);
read();n=read();ans=0ll;
for(int i=1;i<=n;i++){
x[i]=read();ll r=read();
ss[i]=ss[i-1]+x[i]*r;
sr[i]=sr[i-1]+r;
}
ans=ss[n];l=x[1];r=x[n];
// printf("l+r=%lld\n",l+r);
for(int i=1;i<1001;i++){
lmid=(l+r)/2ll;
rmid=(lmid+r)/2ll;
if (F(lmid)>=F(rmid))
l=lmid;
else r=rmid;
}
for(ll i=l;i<=r;i++)
ans=min(ans,F(i));
printf("%lld",ans);
return 0;
}