Gym

题意:有一个序列,一开始所有的元素都是ai,你可以选择两个长度相等的区间,如果某个元素被一个区间覆盖,那么变为bi,如果被两个区间都覆盖,那么变为ci。问所有区间的选择方法中产生的第k小的元素总和。

首先很容易想到的是二分答案,枚举一个区间,然后用线段树或树状数组查询另一个区间使得元素总和小于等于答案的数量。

为了方便计算,我们把b和c数组中的元素都减掉a,即bi=bi-ai,ci=ci-ai,这样只要查询两区间中元素之和小于等于答案的数量就行了,最后再把答案加上a中元素的和。

由于序列是静态的,因此可以对b和c求个前缀和。

当两区间不相交时,只要把右边的区间从左往右移动,并把左边的区间和动态加入树状数组中查询即可。

当两区间相交时,问题就比较复杂了。按照官方题解中的方法,需要引进两个辅助函数g和f,两者相加会产生"bcb"的效果,这样就又可以用树状数组统计了。

其中$g_x=sumlimits _{i=1}^{x-1}{(c_i-2b_i)}+sumlimits _{i=x}^{x+r-1}{(c_i-b_i)}$

$f_y=sumlimits _{i=1}^{y-1}{(2b_i-c_i)}+sumlimits _{i=y}^{y+r-1}{b_i}$

推导过程:

设$g_x=sumlimits _{i=1}^{x-1}{g_1x}+sumlimits _{i=x}^{x+r-1}{g_2x}$

$f_y=sumlimits _{i=1}^{y-1}{f_1y}+sumlimits _{i=y}^{y+r-1}{f_2y}$

则有:$left{egin{matrix}egin{aligned}&f_1+g_1=0\&f_1+g_2=b\&f_2+g_2=c\&f_2=bend{aligned}end{matrix} ight.$

联立方程组即可求出$f_1,f_2,g_1,g_2$的值。

最后要注意的是,由于需要在树状数组上查询的值最多只有O(n)种,因此可以将要查询的值离散化。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const ll N=3e4+10,inf=0x3f3f3f3f3f3f3f3fll;
 5 ll n,len,k,a[N],b[N],c[N],C[N*3],B[N*3],n2,F[N],G[N],K[N],all;
 6 ll lb(ll x) {return x&-x;}
 7 void add(ll u,ll x) {for(; u<=n2; u+=lb(u))C[u]+=x;}
 8 ll get(ll u) {ll ret=0; for(; u; u-=lb(u))ret+=C[u]; return ret;}
 9 ll sum(ll* t,ll l,ll r) {return t[r]-t[l-1];}
10 ll g(ll x) {return sum(c,1,x-1)-2*sum(b,1,x-1)+sum(c,x,x+len-1)-sum(b,x,x+len-1);}
11 ll f(ll x) {return 2*sum(b,1,x-1)-sum(c,1,x-1)+sum(b,x,x+len-1);}
12 ll qry(ll x) {
13     ll cnt=0;
14     for(ll i=0; i<=n2; ++i)C[i]=0;
15     for(ll i=len+1; i+len-1<=n; ++i) {
16         add(K[i-len]+1,1);
17         cnt+=get(upper_bound(B,B+n2,x-B[K[i]])-B);
18     }
19     for(ll i=0; i<=n2; ++i)C[i]=0;
20     for(ll i=2; i+len-1<=n; ++i) {
21         add(G[i-1]+1,1);
22         if(i-len>=1)add(G[i-len]+1,-1);
23         cnt+=get(upper_bound(B,B+n2,x-B[F[i]])-B);
24     }
25     return cnt;
26 }
27 ll bi(ll l,ll r) {
28     while(l<r) {
29         ll mid=l+((r-l)>>1);
30         k<=qry(mid)?r=mid:l=mid+1;
31     }
32     return l;
33 }
34 int main() {
35     scanf("%lld%lld%lld",&n,&len,&k);
36     for(ll i=1; i<=n; ++i)scanf("%lld",&a[i]);
37     for(ll i=1; i<=n; ++i)scanf("%lld",&b[i]);
38     for(ll i=1; i<=n; ++i)scanf("%lld",&c[i]);
39     for(ll i=1; i<=n; ++i)all+=a[i];
40     for(ll i=1; i<=n; ++i)c[i]-=a[i],b[i]-=a[i];
41     for(ll i=1; i<=n; ++i)b[i]+=b[i-1],c[i]+=c[i-1];
42     for(ll i=1; i+len-1<n; ++i)G[i]=g(i),B[n2++]=G[i];
43     for(ll i=2; i+len-1<=n; ++i)F[i]=f(i),B[n2++]=F[i];
44     for(ll i=1; i+len-1<=n; ++i)K[i]=sum(b,i,i+len-1),B[n2++]=K[i];
45     B[n2++]=~inf,B[n2++]=inf;
46     sort(B,B+n2),n2=unique(B,B+n2)-B;
47     for(ll i=1; i+len-1<n; ++i)G[i]=lower_bound(B,B+n2,G[i])-B;
48     for(ll i=2; i+len-1<=n; ++i)F[i]=lower_bound(B,B+n2,F[i])-B;
49     for(ll i=1; i+len-1<=n; ++i)K[i]=lower_bound(B,B+n2,K[i])-B;
50     printf("%lld
",all+bi(0,c[n]));
51     return 0;
52 }
原文地址:https://www.cnblogs.com/asdfsag/p/11262117.html