zoukankan      html  css  js  c++  java
  • 【题解】【原创题目】せやな~

    【题解】【原创题目】せやな~

    出题人:辰星凌

    验题人:( ext{YudeS})

    题目传送门:せやな~

    【题目描述】

    简化题意:求给定序列中的第 (K) 大区间和前 (K) 大区间和。

    【分析】

    【Solution #1】

    纯暴力,把所有区间权值算出来排个序取前 (K) 个即可,注意开 ( ext{long long})

    时间复杂度:(O(n^2logn))

    分数:(10pt)

    【Code #1】
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=1e5+5,M=1e6+5,inf=1e18,P=99999999999999997ll;
    LL n,m,K,op,A[N],S[N],Ans[M];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline bool cmp(Re a,Re b){return a>b;}
    inline void out(){
        printf("%lld
    ",Ans[K]);
        if(op){
            LL ans=0;
            for(Re i=1;i<=K;++i)(ans+=(Ans[i]+P)%P)%=P;
            printf("%lld
    ",ans);
        }
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op);
        for(Re i=1;i<=n;++i)in(A[i]),S[i]=S[i-1]+A[i];
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=i;++j)
                Ans[++m]=S[i]-S[j-1];
        sort(Ans+1,Ans+m+1,cmp);
        out();
    }
    

    【Solution #2】

    注意到一个很有用的性质: 对于每个 (i in [1,n]),以 (i) 为右端点的区间中,对答案有贡献的一定不会超过 (K) 个。

    (O(nlogn)) 预处理出一个数组 (nex),其中 (nex[i]) 表示比 (i) 大的数中最小的一个位置,如果相同则取靠左边的。

    (1)(n) 枚举 (i),对于每个 (i) 都用 (nex) 暴跳一波,如果有位置 (j)(i) 左边,就把 (S[i]-S[j]) 加进队列 (B_1),然后再与答案队列 (Ans) 合并成新的答案队列,可知 (B_1,Ans) 均保持单调性,直接 (O(K)) 归并即可。

    对于每个指针,最多可能会移 (O(n)) 次,但位于 (i) 左边的超过 (K) 个时就会停止,所以时间复杂度不太好分析,大约为 (O(ksim n)) 。我并没有特意去卡这种做法,只造了我自己的代码过不了的数据,或许卡下常能多水过一个点?

    时间复杂度:(O(nlogn+(nk sim n^2)+nk))

    分数:(25pt hicksim 40pt)

    【Code #2-1】
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=1e5+5,M=1000+5,inf=1e18,P=99999999999999997ll;
    LL n,m,K,op,A[N],S[N],B1[M],B2[M],nex[N],Ans[M];
    struct QAQ{
        LL x,v;QAQ(LL X=0,LL V=0){x=X,v=V;}
        inline bool operator<(const QAQ &O)const{return v!=O.v?v<O.v:x<O.x;}
    }b[N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline int merge(Re *P,Re *A,Re t1,Re *B,Re t2){//归并
        Re t=0,p1=1,p2=1;A[t1+1]=B[t2+1]=-inf;
        while(t<K&&(p1<=t1||p2<=t2))//注意t>=K时要停止
            if(A[p1]>B[p2])P[++t]=A[p1++];
            else P[++t]=B[p2++];
        return t;
    }
    inline void out(){
        printf("%lld
    ",Ans[K]);
        if(op){
            LL ans=0;
            for(Re i=1;i<=K;++i)(ans+=(Ans[i]+P)%P)%=P;
            printf("%lld
    ",ans);
        }
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op);
        for(Re i=1;i<=n;++i)in(A[i]),S[i]=S[i-1]+A[i];
        for(Re i=0;i<=n;++i)b[++m]=QAQ(i,S[i]);
        sort(b+1,b+m+1);//从小到大排序
        nex[b[m].x]=n+1;//注意边界
        for(Re i=m-1;i>=1;--i)nex[b[i].x]=b[i+1].x;
        for(Re i=1;i<=n;++i){
            Re t1=0,t2=Ans[0];Ans[0]=0;
            for(Re j=b[1].x;t1<K&&j!=n+1;j=nex[j])
                if(j<i)B1[++t1]=S[i]-S[j];//从大到小存进去
            for(Re j=1;j<=t2;++j)B2[j]=Ans[j];
            Ans[0]=merge(Ans,B1,t1,B2,t2);
        }
        out();
    }
    

    【优化】

    考虑去掉上面那个 (O(frac{n^2}{ ext{玄学}})) 的指针转移。

    对于一个 (i),只要求出了它左边前 (K) 小的 (S[j]),就能将其变为前 (K) 大的 (S[i]-S[j]) 。前面使用 (B_1) 存了区间权值,那么如果我们用它来存 (S[j]) 呢?

    (i) 向后移一位时,我们发现 (B_{1}) 中的大部分元素都没变,只是多出了一个 (S[i-1]),可以通过枚举或者二分找到我们需要插♂入的位置,然后将它塞进去。

    上述操作均可 (O(k)) 实现。

    时间复杂度:(O(nk))

    分数:(40pt)

    【Code #2-2】
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=1e5+5,M=1000+5,inf=1e18,P=99999999999999997ll;
    LL n,m,K,op,t1,t2,A[N],S[N],Q[M],B1[M],B2[M],nex[N],Ans[M];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline int merge(Re *P,Re *A,Re t1,Re *B,Re t2){//归并
        Re t=0,p1=1,p2=1;A[t1+1]=B[t2+1]=-inf;
        while(t<K&&(p1<=t1||p2<=t2))
            if(A[p1]>B[p2])P[++t]=A[p1++];
            else P[++t]=B[p2++];
        return t;
    }
    inline void out(){
        printf("%lld
    ",Ans[K]);
        if(op){
            LL ans=0;
            for(Re i=1;i<=K;++i)(ans+=(Ans[i]+P)%P)%=P;
            printf("%lld
    ",ans);
        }
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op);
        for(Re i=1;i<=n;++i)in(A[i]),S[i]=S[i-1]+A[i];
        for(Re i=1;i<=n;++i){
            if(t1<K)Q[++t1]=inf;//如果还没有K个,增加一个位置
            for(Re j=1;j<=t1;++j)
                if(S[i-1]<Q[j]){//找到需要插♂入的位置
                    for(Re k=t1-1;k>=j;--k)Q[k+1]=Q[k];//后面整体向后移一位腾出位置
                    Q[j]=S[i-1];break;//插♂入S[i-1]
                }
            Re t2=Ans[0];
            for(Re j=1;j<=t1;++j)B1[j]=S[i]-Q[j];
            for(Re j=1;j<=t2;++j)B2[j]=Ans[j];
            Ans[0]=merge(Ans,B1,t1,B2,t2);
        }
        out();
    }
    

    【Solution #3】

    特殊性质 (1)(forall i in [1,n],) (a_{i} geqslant 0)

    (a_{i}) 均大于 (0),有什么用?

    任意一个区间的权值都一定不会比它的子区间权值小。

    最大的区间一定是 ([1,n]),先把 ([1,n]) 丢进一个按区间权值排序的大根堆。不断取堆顶的元素直至 (K) 次,然后将取出来的区间 ([l,r]) 变成 ([l,r-1])([l+1,r]) 丢进去。

    注意 ([l+1,r])([l,r-1]) 都有可能产生 ([l+1,r-1]),所以每次加入时都要判断该区间是否已经被加过。

    时间复杂度:(O(KlogK))

    分数:(10pt),结合之前的算法可以得到 (50pt)

    【Code #3-1】
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #include<map>
    #define LL long long
    #define Re register LL
    #define mp make_pair
    using namespace std;
    const LL N=1e5+6,M=1e6+5,inf=1e18,P=99999999999999997ll;
    LL n,K,op,flag=1,A[N],S[N],Ans[M];
    struct QAQ{
        LL l,r,v;QAQ(LL L=0,LL R=0){v=S[r=R]-S[(l=L)-1];}
        inline bool operator<(const QAQ &O)const{return v<O.v;}
    };
    priority_queue<QAQ>Q;map<pair<LL,LL>,bool>pan;
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline void sakura(){//求前K大
        Q.push(QAQ(1,n));
        while(Ans[0]<K){
            QAQ x=Q.top();Q.pop();
            Ans[++Ans[0]]=x.v;
            if(x.l!=x.r){
                if(!pan[mp(x.l,x.r-1)])pan[mp(x.l,x.r-1)]=1,Q.push(QAQ(x.l,x.r-1));
                if(!pan[mp(x.l+1,x.r)])pan[mp(x.l+1,x.r)]=1,Q.push(QAQ(x.l+1,x.r));
            }
        }
    }
    inline void out(){
        printf("%lld
    ",Ans[K]);
        if(op){
            LL ans=0;
            for(Re i=1;i<=K;++i)(ans+=(Ans[i]+P)%P)%=P;
            printf("%lld
    ",ans);
        }
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op);
        for(Re i=1;i<=n;++i)in(A[i]),S[i]=S[i-1]+A[i],flag&=(A[i]>=0);
        if(flag){sakura(),out();return 0;}
    }
    

    【算法改进】

    考虑上述算法如何一般化。

    类似 超级钢琴 ( ext{[NOI2010] [P2048]}),用大根堆维护四元组 ({x,l,r,t}),其中 (t) 为区间 ([l,r]) 中最大前缀和 (S) 的位置,可以用 (st) 表预处理。堆按照 (S[t]-S[x]) 的大小排序。

    对于每个 (i),将 ({i,i,n,t}) 加入大根堆,堆按照 (S[t]-S[x]) 的大小排序。每次取出堆顶元素,然后将取出来的区间 ([l,r]) 变为 ([l,t-1])([t+1,r]) 重新加进去。

    代码超简单。

    时间复杂度:(O(nlogn+KlogK))

    分数:(70pt)

    【Code #3-2】
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #define LL long long
    #define Re register int
    using namespace std;
    const LL N=1e5+5,M=1e6+5,logN=17,inf=1e18,P=99999999999999997ll;
    int n,K,op,A[N],lg[N],f[N][18];LL S[N],Ans[M];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline int Max(Re a,Re b){return S[a]>S[b]?a:b;}
    inline int ask(Re L,Re R){Re m=lg[R-L+1];return Max(f[L][m],f[R-(1<<m)+1][m]);}
    struct QAQ{
        int x,t,l,r;QAQ(int X=0,int L=0,int R=0){x=X,t=ask(l=L,r=R);}
        inline bool operator<(const QAQ &O)const{return S[t]-S[x-1]<S[O.t]-S[O.x-1];}
    };
    priority_queue<QAQ>Q;
    inline void out(){
        printf("%lld
    ",Ans[K]);
        if(op){
            LL ans=0;
            for(Re i=1;i<=K;++i)(ans+=(Ans[i]+P)%P)%=P;
            printf("%lld
    ",ans);
        }
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op),lg[0]=-1;
        for(Re i=1;i<=n;++i)in(A[i]),S[f[i][0]=i]=S[i-1]+A[i],lg[i]=lg[i>>1]+1;
        for(Re j=1;j<=logN;++j)
            for(Re i=1;i+(1<<j)-1<=n;++i)
                f[i][j]=Max(f[i][j-1],f[i+(1<<j-1)][j-1]);
        for(Re i=1;i<=n;++i)Q.push(QAQ(i,i,n));
        while(Ans[0]<K){
            QAQ x=Q.top();Q.pop();
            Ans[++Ans[0]]=S[x.t]-S[x.x-1];
            if(x.t!=x.l)Q.push(QAQ(x.x,x.l,x.t-1));
            if(x.t!=x.r)Q.push(QAQ(x.x,x.t+1,x.r));
        }
        out();
    }
    

    【Solution #4】

    特殊性质 (2):只需要求输出 (K) 大区间,不用输出前 (K) 大区间的权值和。

    求第 (K) 大可以用二分啊!

    二分一个 (limit),每次计算权值大于等于 (limit) 的区间个数,如果大于等于 (K) 就调下界,否则调上界。

    (judge) 函数随便弄个什么数据结构就搞过去了,而且还是很明显的 ( ext{CDQ}) 板子,但同样的复杂度,离散化 (+) 树状数组要快一倍。

    注意写树状数组时不能在 (judge) 函数中进行离散化,否则会平白无故多出一个 (O(logn)),由于 (S) 数组均减去(加上)(limit) 后排名关系不变,所以可以提前预处理处 (S) 的排名,以此获得 (S) 减去(加上) (limit) 后的排名,然后用归并将两部分合起来得到最终使用的离散排名。

    时间复杂度:(O(nlognloginf))

    分数:(10pt),结合之前的算法可以得到 (80pt)

    【Code #4-1】

    (这是树状数组的写法,( ext{CDQ}) 做法与下面的 ( ext{Code #4-3}) 放一起了)

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=1e6+5,M=1e6+5,inf=1e18,P=99999999999999997ll;
    LL n,m,K,op,Ans0,Max,Min,A[N],S[N],rk[N][2];
    struct QAQ{
        LL i,S,rk;bool o;
        inline bool operator<(const QAQ &O)const{return S<O.S;}
    }S1[N],S2[N],S0[N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    struct BIT{
        LL n,C[N<<1];
        inline void CL(Re m){n=m;for(Re i=1;i<=n;++i)C[i]=0;}
        inline void add(Re x){while(x<=n)++C[x],x+=x&-x;}
        inline LL ask(Re x){Re ans=0;while(x)ans+=C[x],x-=x&-x;return ans;}
    }T1;
    inline LL merge_(QAQ *P,QAQ *A,Re t1,QAQ *B,Re t2){//归并
        Re t=0,t_=0,p1=1,p2=1;A[t1+1].S=B[t2+1].S=inf;
        while((p1<=t1||p2<=t2))
            if(A[p1].S<B[p2].S)P[++t]=A[p1++];
            else P[++t]=B[p2++];
        for(Re i=1;i<=t;++i){
            if(i<2||P[i].S!=P[i-1].S)++t_;
            P[i].rk=t_;
        }
        return t_;
    }
    inline LL judge(Re mid){
        for(Re i=1;i<=m;++i)S2[i]=S1[i],S2[i].S-=mid,S2[i].o=1;//S1本身有序,S1.S[i]均减去mid后依然有序
        Re tmp=0,t_=merge_(S0,S1,m,S2,m);T1.CL(t_);//把两个有序的S1,S2合成一个S0,可以直接归并
        for(Re i=1;i<=(m<<1);++i)rk[S0[i].i][S0[i].o]=S0[i].rk;//获得排名
        for(Re i=1;i<=n&&tmp<K;++i)T1.add(rk[i-1][0]),tmp+=T1.ask(rk[i][1]);//统计大于等于mid的区间数量
        return tmp>=K;
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op);
        for(Re i=1,Mi=0,Ma=0;i<=n;++i)
            in(A[i]),S[i]=S[i-1]+A[i],
            Max=max(Max,S[i]-Mi),Mi=min(Mi,S[i]),
            Min=min(Min,S[i]-Ma),Ma=max(Ma,S[i]);
        for(Re i=0;i<=n;++i)S1[++m]=(QAQ){i,S[i]};
        sort(S1+1,S1+m+1);//让S1有序
        Re l=Min,r=Max;//卡一下常数
        while(l<r){
            Re mid=l+r+1>>1;
            if(judge(mid))l=mid;
            else r=mid-1;
        }
        printf("%lld
    ",Ans0=l);
    }
    

    【算法改进】

    解决了特殊性质 (2),正解自然就出来了。

    先算出第 (K) 大权值 (Ans_0)

    依旧是上 ( ext{CDQ}) 板子:计算权值严格大于 (Ans_0) 的区间权值和。

    最后再加上 (Ans_0) ( imes) ((K-) 权值大于等于 (Ans_0+1) 的区间个数 ())

    时间复杂度:(O(nlognloginf+nlogn))

    分数:(100pt)

    【Code #4-2】

    (树状数组 (judge)

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=1e5+5,inf=1e18,P=99999999999999997ll;
    LL n,m,K,op,Max,Min,Ans0,Ans1,A[N],S[N],S_[N],rk[N][2];
    struct QAQ{
        LL i,S,rk;bool o;
        inline bool operator<(const QAQ &O)const{return S<O.S;}
    }S1[N],S2[N],S0[N<<1];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    struct BIT{
        LL n,C[N<<1];
        inline void CL(Re m){n=m;for(Re i=1;i<=n;++i)C[i]=0;}
        inline void add(Re x){while(x<=n)++C[x],x+=x&-x;}
        inline LL ask(Re x){Re ans=0;while(x)ans+=C[x],x-=x&-x;return ans;}
    }T1;
    inline LL merge_(QAQ *P,QAQ *A,Re t1,QAQ *B,Re t2){//归并
        Re t=0,t_=0,p1=1,p2=1;A[t1+1].S=B[t2+1].S=inf;
        while((p1<=t1||p2<=t2))
            if(A[p1].S<B[p2].S)P[++t]=A[p1++];
            else P[++t]=B[p2++];
        for(Re i=1;i<=t;++i){
            if(i<2||P[i].S!=P[i-1].S)++t_;
            P[i].rk=t_;
        }
        return t_;
    }
    inline LL judge(Re mid){
        for(Re i=1;i<=m;++i)S2[i]=S1[i],S2[i].S-=mid,S2[i].o=1;//S1本身有序,S1.S[i]均减去mid后依然有序
        Re tmp=0,t_=merge_(S0,S1,m,S2,m);T1.CL(t_);//把两个有序的S1,S2合成一个S0,可以直接归并
        for(Re i=1;i<=(m<<1);++i)rk[S0[i].i][S0[i].o]=S0[i].rk;//获得排名
        for(Re i=1;i<=n&&tmp<K;++i)T1.add(rk[i-1][0]),tmp+=T1.ask(rk[i][1]);//统计大于等于mid的区间数量
        return tmp;
    }
    inline void merge(Re *P,Re p1,Re t1,Re p2,Re t2){//归并
        Re t=p1-1;
        while((p1<=t1||p2<=t2))
            if((p1<=t1&&S[p1]<S[p2])||p2>t2)P[++t]=S[p1++];//注意判断是否大于t1,t2
            else P[++t]=S[p2++];
    }
    inline void CDQ(Re L,Re R){
        if(L==R)return;
        Re mid=L+R>>1,p1=L-1,p2=mid+1,SL=0;
        CDQ(L,mid),CDQ(mid+1,R);
        while(p2<=R){
            while(p1<mid&&S[p2]-S[p1+1]>Ans0)SL+=S[++p1];//移左指针
            (Ans1+=(S[p2++]*(p1-L+1)-SL+P)%P)%=P;//右指针一位一位地移
        }
        merge(S_,L,mid,mid+1,R);//归并排序
        for(Re i=L;i<=R;++i)S[i]=S_[i];
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op);
        for(Re i=1,Mi=0,Ma=0;i<=n;++i)
            in(A[i]),S[i]=S[i-1]+A[i],
            Max=max(Max,S[i]-Mi),Mi=min(Mi,S[i]),
            Min=min(Min,S[i]-Ma),Ma=max(Ma,S[i]);
        for(Re i=0;i<=n;++i)S1[++m]=(QAQ){i,S[i]};
        sort(S1+1,S1+m+1);//让S1有序
        Re l=Min,r=Max;//卡一下常数
        while(l<r){
            Re mid=l+r+1>>1;
            if(judge(mid)>=K)l=mid;
            else r=mid-1;
        }
        printf("%lld
    ",Ans0=l);
        if(op){
            K-=judge(Ans0+1),CDQ(0,n);
            printf("%lld
    ",(Ans1+K*Ans0%P)%P);//其实K*Ans0是有可能会炸的(10^19),懒得写龟速乘和专门出数据了
        }
    }
    
    【Code #4-3】

    ( ext{CDQ}) (judge)

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=1e5+5,inf=1e18,P=99999999999999997ll;
    LL n,m,K,op,tmp,Max,Min,Ans0,Ans1,A[N],S[N],S_[N],S__[N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline void merge_(Re *P,Re p1,Re t1,Re p2,Re t2){//归并
        Re t=p1-1;
        while((p1<=t1||p2<=t2))
            if((p1<=t1&&S_[p1]<S_[p2])||p2>t2)P[++t]=S_[p1++];//注意判断是否大于t1,t2
            else P[++t]=S_[p2++];
    }
    inline void CDQ_(Re L,Re R,Re limit){
        if(L==R)return;
        Re mid=L+R>>1,p1=L-1,p2=mid+1;
        CDQ_(L,mid,limit),CDQ_(mid+1,R,limit);
        while(p2<=R){
            while(p1<mid&&S_[p2]-S_[p1+1]>=limit)++p1;//移左指针
            tmp+=p1-L+1,++p2;//右指针一位一位地移
        }
        merge_(S__,L,mid,mid+1,R);//归并排序
        for(Re i=L;i<=R;++i)S_[i]=S__[i];
    }
    inline LL judge(Re mid){
        tmp=0;
        for(Re i=0;i<=n;++i)S_[i]=S[i];
        CDQ_(0,n,mid);
        return tmp;
    }
    inline void merge(Re *P,Re p1,Re t1,Re p2,Re t2){//归并
        Re t=p1-1;
        while((p1<=t1||p2<=t2))
            if((p1<=t1&&S[p1]<S[p2])||p2>t2)P[++t]=S[p1++];//注意判断是否大于t1,t2
            else P[++t]=S[p2++];
    }
    inline void CDQ(Re L,Re R){
        if(L==R)return;
        Re mid=L+R>>1,p1=L-1,p2=mid+1,SL=0;
        CDQ(L,mid),CDQ(mid+1,R);
        while(p2<=R){
            while(p1<mid&&S[p2]-S[p1+1]>Ans0)SL+=S[++p1];//移左指针
            (Ans1+=(S[p2++]*(p1-L+1)-SL+P)%P)%=P;//右指针一位一位地移
        }
        merge(S_,L,mid,mid+1,R);//归并排序
        for(Re i=L;i<=R;++i)S[i]=S_[i];
    }
    int main(){
    //  freopen("data.in","r",stdin);
        in(n),in(K),in(op);
        for(Re i=1,Mi=0,Ma=0;i<=n;++i)
            in(A[i]),S[i]=S[i-1]+A[i],
            Max=max(Max,S[i]-Mi),Mi=min(Mi,S[i]),
            Min=min(Min,S[i]-Ma),Ma=max(Ma,S[i]);
        Re l=Min,r=Max;//卡一下常数
        while(l<r){
            Re mid=l+r+1>>1;
            if(judge(mid)>=K)l=mid;
            else r=mid-1;
        }
        printf("%lld
    ",Ans0=l);
        if(op){
            K-=judge(Ans0+1),CDQ(0,n);
            printf("%lld
    ",(Ans1+K*Ans0%P)%P);//其实K*Ans0是有可能会炸的(10^19),懒得写龟速乘和专门出数据了
        }
    }
    

    【一些题外话】

    感谢 ( ext{YudeS}) 提出 ( ext{Code #3-2}) 中的做法。

    这道题是在考试期间闲得无聊 (yy) 出来的。

  • 相关阅读:
    204. Count Primes (Integer)
    203. Remove Linked List Elements (List)
    202. Happy Number (INT)
    201. Bitwise AND of Numbers Range (Bit)
    200. Number of Islands (Graph)
    199. Binary Tree Right Side View (Tree, Stack)
    198. House Robber(Array; DP)
    191. Number of 1 Bits (Int; Bit)
    190. Reverse Bits (Int; Bit)
    189. Rotate Array(Array)
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12173748.html
Copyright © 2011-2022 走看看