zoukankan      html  css  js  c++  java
  • EZOJ #77

    传送门

    分析

    一个比较神奇的思路

    我们考虑分治,对于每一个区间[le,ri]我们计算这个区间中左端点属于[le,mid],右端点属于[mid+1,ri]的情况对答案的贡献

    我们求左半个区间的最大最小值的后缀信息以及右半个区间的最大最小值的前缀信息

    于是我们发现在左半面最大值越来越小、最小值越来越大,右半面反之

    于是我们枚举左端点,并由这个点i找到它在右半个区间对应的p和q

    p表示右面最靠左的大于premax[i]的点,q表示右面最靠左的小于premin[i]的点

    然后我们分p<=q和p>q两种情况统计答案即可

    注意为了方便起见我们提前处理右半个区间最大值、最小值、最大值乘最小值这三个值的前缀和信息

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cctype>
    #include<cmath>
    #include<cstdlib>
    #include<ctime>
    #include<queue>
    #include<vector>
    #include<set>
    #include<map>
    #include<stack>
    using namespace std;
    const int mod = 998244353;
    int n,a[500100],premin[500100],surmin[500100],premax[500100],surmax[500100],Ans;
    int s1[500100],s2[500100],s3[500100];
    inline void go(int le,int ri){
        if(le==ri){
          Ans=(Ans+(long long)a[le]*a[le]%mod)%mod;
          return;
        }
        int i,mid=(le+ri)>>1,p=mid+1,q=mid+1;
        premin[mid]=premax[mid]=surmin[mid]=surmax[mid]=a[mid];
        for(i=mid-1;i>=le;i--){
          premin[i]=min(premin[i+1],a[i]);
          premax[i]=max(premax[i+1],a[i]);
        }
        for(i=mid+1;i<=ri;i++){
          surmin[i]=min(surmin[i-1],a[i]);
          surmax[i]=max(surmax[i-1],a[i]);
        }
        s1[mid]=s2[mid]=s3[mid]=0;
        for(i=mid+1;i<=ri;i++){
          s1[i]=(s1[i-1]+surmax[i])%mod;
          s2[i]=(s2[i-1]+surmin[i])%mod;
          s3[i]=(s3[i-1]+(long long)surmin[i]*surmax[i]%mod)%mod;
        }
        for(i=mid;i>=le;i--){
          while(surmax[p]<premax[i]&&p<=ri)p++;
          while(surmin[q]>premin[i]&&q<=ri)q++;
          int tot=0;
          if(p<=q){
              tot=(long long)(p-mid-1)*premin[i]%mod*premax[i]%mod;
              tot=(tot+(long long)premin[i]*((s1[q-1]-s1[p-1])%mod+mod)%mod)%mod;
            tot=(tot+((s3[ri]-s3[q-1])%mod+mod)%mod)%mod;
          }else {
              tot=(long long)(q-mid-1)*premin[i]%mod*premax[i]%mod;
              tot=(tot+(long long)premax[i]*((s2[p-1]-s2[q-1])%mod+mod)%mod)%mod;
            tot=(tot+((s3[ri]-s3[p-1])%mod+mod)%mod)%mod;
          }
          Ans=(Ans+tot)%mod;
        }
        go(le,mid),go(mid+1,ri);
        return;
    }
    int main(){
        int i,j,k;
        scanf("%d",&n);
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        go(1,n);
        printf("%d
    ",Ans);
        return 0;
    }
  • 相关阅读:
    第4章 栈和队列
    第3章 线性表
    第2章 算法
    第1章 数据结构绪论
    First Blood
    第52条:通过接口引用对象
    第51条:当心字符串连接的性能
    第50条:如果其他类型更合适,则尽量避免使用字符串
    第49条:基本类型优先于装箱基本类型
    第48条:如果需要精确的答案,请避免使用float和double
  • 原文地址:https://www.cnblogs.com/yzxverygood/p/9880197.html
Copyright © 2011-2022 走看看