Description
Y sera 陷入了沉睡,幻境中它梦到一个长度为N 的序列{Ai}。
对于这个序列的每一个子串,定义其幻境值为这个子串的和,现在Y sera 希望选择K 个不同的子串并使得这K 个子串的幻境值之和最大。
然而由于梦境中的种种限制,这些子串的长度必须在L 到R 之间。
你需要告诉她,最大的幻境值之和。
Solution1
记
s
i
=
∑
i
=
1
n
a
i
s_i=\sum_{i=1}^na_i
si=∑i=1nai
枚举子串开头
i
i
i,子串结尾为
j
∈
[
i
+
l
−
1
,
i
+
r
−
1
]
j\in[i+l-1,i+r-1]
j∈[i+l−1,i+r−1],子串和显然为
s
j
−
s
i
−
1
s_j-s_{i-1}
sj−si−1,我们可以找到一个
j
j
j使得
s
j
−
s
i
−
1
s_j-s_{i-1}
sj−si−1最大,这个可以用
r
m
q
rmq
rmq完成,将它们都放进一个大根堆里面
每次都从堆顶取出最优值,加入答案,记其开头为
i
i
i,结尾为
j
j
j,结尾范围为
l
∼
r
l\sim r
l∼r,
j
j
j用过了后,结尾范围就变成了
[
l
,
j
−
1
]
∪
[
j
+
1
,
r
]
[l,j-1]\cup [j+1,r]
[l,j−1]∪[j+1,r],再用
r
m
q
rmq
rmq求一次
[
l
,
j
−
1
]
[l,j-1]
[l,j−1]和
[
j
+
1
,
r
]
[j+1,r]
[j+1,r]的最优解,放进堆里。
运行
k
k
k遍就能求出答案,时间复杂度为
O
(
k
log
n
)
O(k\log n)
O(klogn)
Solution2
记
f
(
x
)
f(x)
f(x)为
x
x
x在所有合法子串中的排名,显然
f
(
x
)
f(x)
f(x)具有单调性,只要我们可以求出
f
(
x
)
f(x)
f(x)就可以用二分求得答案
分别计算每个开头
i
i
i,子串和大于
x
x
x的数有多少个,记子串结尾为
j
∈
[
i
+
l
−
1
,
i
+
r
−
1
]
j\in[i+l-1,i+r-1]
j∈[i+l−1,i+r−1],我们就是要找出使得
x
≤
s
j
−
s
i
−
1
x\le s_j-s_{i-1}
x≤sj−si−1,即
x
+
s
i
−
1
≤
s
j
x+s_{i-1}\le s_j
x+si−1≤sj的
j
j
j有多少个,
x
+
s
i
−
1
x+s_{i-1}
x+si−1已知,所以这个可以用主席树完成,时间复杂度为
O
(
n
log
n
)
O(n\log n)
O(nlogn),套上二分就是
O
(
n
log
2
n
)
O(n\log^2 n)
O(nlog2n)
总结
由于本题 n n n与 k k k同级,所以方法一更优,但当 k k k达到 n 2 n^2 n2级别的时候,方法二会优的多
Code
下面给出一份方法一的代码
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int N=2e5+1000;
int n,k,l,r,lg[N+10];
ll s[30][N+10],t[N+10],g[30][N+10];
void rmq()
{
for(int i=1;i<=lg[n];i++)
for(int j=1;j+(1<<i)-1<=n;j++)
{
if(s[i-1][j]>s[i-1][j+(1<<i-1)])
s[i][j]=s[i-1][j],g[i][j]=g[i-1][j];
else
s[i][j]=s[i-1][j+(1<<i-1)],g[i][j]=g[i-1][j+(1<<i-1)];
}
}
int querypos(int l,int r){return s[lg[r-l+1]][l]>s[lg[r-l+1]][r-(1<<lg[r-l+1])+1]?g[lg[r-l+1]][l]:g[lg[r-l+1]][r-(1<<lg[r-l+1])+1];}
struct node
{
int pos,i,l,r;
};
struct cmp
{
bool operator() (node a,node b){return s[0][a.i]-s[0][a.pos]<s[0][b.i]-s[0][b.pos];}
};
priority_queue<node,vector<node>,cmp> h;
int main()
{
// freopen("fantasy.in","r",stdin);
// freopen("fantasy.out","w",stdout);
scanf("%d %d %d %d",&n,&k,&l,&r);
for(int i=1;i<=n;i++)
scanf("%lld",&s[0][i]),s[0][i]+=s[0][i-1],g[0][i]=i;
// for(int i=1;i<=n;i++)
// printf("%lld ",s[0][i]);
for(int i=2;i<=n;i++)
lg[i]=lg[i-1]+(i==(1<<lg[i-1]+1));
rmq();
for(int i=0;i<=n-l;i++)
{
h.push((node){i,querypos(i+l,min(n,i+r)),i+l,min(n,i+r)});
// printf("%d %d\n",i,querypos(i+l,min(n,i+r)));
}
ll ans=0;
for(;k--&&!h.empty();)
{
node u=h.top();h.pop();
// printf("%d %d\n",u.pos,u.i);
ans+=s[0][u.i]-s[0][u.pos];
if(u.i-1>=u.l) h.push((node){u.pos,querypos(u.l,u.i-1),u.l,u.i-1});
if(u.i+1<=u.r) h.push((node){u.pos,querypos(u.i+1,u.r),u.i+1,u.r});
}
printf("%lld\n",ans);
return 0;
}