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) 出来的。

  • 相关阅读:
    1321棋盘问题
    2488A Knight's Journey
    1947Rebuilding Roads
    2057The Lost House
    2196Specialized Four-Digit Numbers
    /etc/sysctl.conf
    运维五步走
    虚拟机无法获得使用权问题
    ST/FC/SC/LC光纤的区别及适用场合;光纤适配器PC、APC和UPC区别
    Unity3D Kinect 控制人物模型
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12173748.html
Copyright © 2011-2022 走看看