Gym101630G The Great Wall

Link
先二分答案(lim),那么我们现在要做的就是判定有多少个对((x,y))满足形成的答案不大于(lim)
我们将所有((x,y))按区间是否相交分为两类,即一类是(yge x+r),另一类是(y<x+r)
先考虑(yge x+r)的情况,设(f_i=sumlimits_{j=i}^{i+r-1}b_j-a_j),那么此时答案为(sumlimits_{i=1}^na_i+f_x+f_y)
预处理前缀和之后求出(f),然后双指针+BIT计算即可。
然后考虑(y<x+r)的情况,设(g_i=sumlimits_{j=1}^{i-1}a_j+c_j-2b_j+sumlimits_{j=i}^{i+r-1}c_j-b_j,h_i=sumlimits_{j=1}^{i-1}2b_j-a_j-c_j+sumlimits_{j=i}^{i+r-1}b_j-a_j),那么此时答案为(sumlimits_{i=1}^na_i+g_x+h_y)
处理方法与上一部分一样。
时间复杂度为(O(nlog nlog ans))

#include<cstdio>
#include<cctype>
#include<cstring>
#include<numeric>
#include<algorithm>
using i64=long long;
const int N=30001;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int n,r,m,k,t[N],p[N],q[N],s[N];
i64 a[N],b[N],c[N],f[N],g[N],h[N];
void add(int p){for(;p<=n;p+=p&-p)++t[p];}
int ask(int p){int r=0;for(;p;p^=p&-p)r+=t[p];return r;}
int query(int l,int r){return l=std::max(1,l),r=std::min(r,n),l>r? 0:ask(r)-ask(l-1);}
int check(i64 lim)
{
    int ans=0;
    memset(t+1,0,n<<2);
    for(int i=m,j=1;i;ans+=query(p[i]+r,m),--i) while(j<=m&&f[p[i]]+f[p[j]]<=lim) add(p[j++]);
    memset(t+1,0,n<<2);
    for(int i=m,j=1;i;ans+=query(q[i]+1,q[i]+r-1),--i) while(j<=m&&g[q[i]]+h[s[j]]<=lim) add(s[j++]);
    return ans>=k;
}
void solve()
{
    i64 l=0,r=c[n]-a[n],ans=0;
    for(i64 mid;l<=r;) check(mid=(l+r)/2)? r=(ans=mid)-1:l=mid+1;
    printf("%lld",ans+a[n]);
}
int main()
{
    n=read(),m=n-(r=read())+1,k=read();
    for(int i=1;i<=n;++i) a[i]=a[i-1]+read();
    for(int i=1;i<=n;++i) b[i]=b[i-1]+read();
    for(int i=1;i<=n;++i) c[i]=c[i-1]+read();
    for(int i=1;i<=m;++i) f[i]=a[i-1]-a[i+r-1]-b[i-1]+b[i+r-1],g[i]=a[i-1]-b[i+r-1]-b[i-1]+c[i+r-1],h[i]=b[i-1]-a[i+r-1]-c[i-1]+b[i+r-1],p[i]=q[i]=s[i]=i;
    std::sort(p+1,p+m+1,[](int i,int j){return f[i]<f[j];}),std::sort(q+1,q+m+1,[](int i,int j){return g[i]<g[j];}),std::sort(s+1,s+m+1,[](int i,int j){return h[i]<h[j];});
    solve();
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12569740.html