【题解】【原创题目】せやな~
出题人:辰星凌
验题人:( 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) 出来的。