[bzoj3745][分治]Norma

7 篇文章 0 订阅

Description

这里写图片描述

Input

第1行,一个整数N; 第2~n+1行,每行一个整数表示序列a。

Output

输出答案对10^9取模后的结果。

Sample Input

4

2

4

1

4

Sample Output

109

HINT

【数据范围】

N <= 500000

1 <= a_i <= 10^8

题解

还是分治
自己yy的东西t的一逼啊233333
对于一段区间 l,r l , r ,找到他的中间点 mid m i d
枚举 l mid l   m i d ,我们的目标是对于所有 [l,mid] [ l , m i d ] 找出他在所有 [mid+1,r] [ m i d + 1 , r ] 区间内的答案
对于每个 i i ,维护两个指针u,v(u,v<=r)
满足 [mid+1,u] [ m i d + 1 , u ] 中的数全部大于 [i,mid] [ i , m i d ] 的最小值且u最大
满足 [mid+1,v] [ m i d + 1 , v ] 中的数全部小于 [i,mid] [ i , m i d ] 的最大值且v最大
于是我们有三种讨论,不妨假设u < v,设 mn m n 表示 [i,mid] [ i , m i d ] 中的最小值, mx m x 表示 [i,mid] [ i , m i d ] 中的最大值
对于 [mid+1,u] [ m i d + 1 , u ] ,他的贡献为

j=mid+1umxmn+(ji+1) ∑ j = m i d + 1 u m x ∗ m n + ( j − i + 1 )

显然可以直接 O(1) O ( 1 )
对于 [u+1,v] [ u + 1 , v ] ,他的贡献为
j=u+1vmxmin[mid+1,j](ji+1) ∑ j = u + 1 v m x ∗ m i n [ m i d + 1 , j ] ∗ ( j − i + 1 )

拆一下就变成了这样
mxj=u+1vmin[mid+1,j]jmin(mid+1,j)(i1) m x ∗ ∑ j = u + 1 v m i n [ m i d + 1 , j ] ∗ j − m i n ( m i d + 1 , j ) ∗ ( i − 1 )

预处理前缀和即 O(1) O ( 1 )
对于 [v+1,r] [ v + 1 , r ] ,他的贡献为
j=v+1rmax[mid+1,j]min(mid+1,j)jmax(mid+1,j)min(mid+1,j)(i1) ∑ j = v + 1 r m a x [ m i d + 1 , j ] ∗ m i n ( m i d + 1 , j ) ∗ j − m a x ( m i d + 1 , j ) ∗ m i n ( m i d + 1 , j ) ∗ ( i − 1 )

预处理前缀和仍然可以 O(1) O ( 1 )
然后又是愉快的 nlogn n l o g n

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
#define lc now<<1
#define rc now<<1|1
using namespace std;
const LL mod=1e9;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n;
LL a[510000];
LL s1[510000],s2[510000],s3[510000],s4[510000],s5[510000],s6[510000],ans;
/*
mn[mid+1,j]*j
mn[mid+1,j]
mx[mid+1,j]*j
mn[mid+1,j]
mx[mid+1,j]*mn[mid+1,j]*j
mx[mid+1,j]*mn[mid+1,j]
*/
LL C(int s,int t){return ((LL)s+t)*((LL)t-s+1)/2%mod;}
void sol(int l,int r)
{
    if(l==r){ans=(ans+a[l]*a[l])%mod;return ;}
    int mid=(l+r)>>1;
    s1[mid]=s2[mid]=s3[mid]=s4[mid]=s5[mid]=s6[mid]=0;
    LL u1=a[mid+1],u2=a[mid+1];
    sol(l,mid);
    sol(mid+1,r);
    for(LL j=mid+1;j<=r;j++)
    {
        u1=min(u1,a[j]);u2=max(u2,a[j]);
        //int u1=findhh(1,1,n,mid+1,j,0),u2=findhh(1,1,n,mid+1,j,1);
        s1[j]=(s1[j-1]+(u1*j%mod))%mod;
        s2[j]=(s2[j-1]+u1)%mod;
        s3[j]=(s3[j-1]+(u2*j%mod))%mod;
        s4[j]=(s4[j-1]+u2)%mod;
        s5[j]=(s5[j-1]+u1*u2%mod*j%mod)%mod;
        s6[j]=(s6[j-1]+u1*u2%mod)%mod;
    }
//  for(int j=mid+1;j<=r;j++)printf("%lld %lld\n",s1[j],s2[j]);
    int u=mid,v=mid;
    LL mn=a[mid],mx=a[mid];
    for(LL i=mid;i>=l;i--)
    {
        mn=min(mn,a[i]);mx=max(mx,a[i]);
        //int mn=findhh(1,1,n,i,mid,0),mx=findhh(1,1,n,i,mid,1);
        while(u<r&&mn<a[u+1])u++;
        while(v<r&&mx>a[v+1])v++;
        ans=(ans+mx*mn%mod*C(mid-i+2,min(u-i+1,v-i+1))%mod)%mod;
        if(u<=v)
        {
            ans=(ans+mx*((s1[v]-s1[u]+mod)%mod-((i-1)*(s2[v]-s2[u]+mod)%mod)%mod)%mod+mod)%mod;
            ans=(ans+((s5[r]-s5[v]+mod)%mod)-((i-1)*(s6[r]-s6[v]+mod)%mod)+mod)%mod;
        }
        else
        {
            ans=(ans+mn*((s3[u]-s3[v]+mod)%mod-((i-1)*(s4[u]-s4[v]+mod)%mod)%mod)%mod+mod)%mod;
            ans=(ans+((s5[r]-s5[u]+mod)%mod)-((i-1)*(s6[r]-s6[u]+mod)%mod)+mod)%mod;
        }
    }
}
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    sol(1,n);
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值