给定n个序列,每个一行,每个序列是可以左右移动的,对于每行可以随意移动的情况下,求每列的和的最大值
n行(1<=n<=10^6) ,w列(1<=w<=10^6)
题解:
首先由于左右边界的限制,最大为w,所以对于每行,可以填进一个列中的数是被限制的。所以我们要找到在这些可选的数中的最大值。因为这些可选的数都是连续的,并且要找最大值,可以想到用ST表查找区间最大值。
对于每个序列分成两种情况:
1.序列长度小于w的一半,这种情况是有某些处于中间的列可以选择所有输入序列的数,开头/结尾的k个直接维护前k个的最大值。影响进行差分处理。
2.长度大于等于w的一半,直接st表求出最大值即可,使用差分维护。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n,w,m,lg[1000100],st[2000100][23];
ll ans[1000100];
inline int query(int l,int r)
{
int k=lg[r-l+1];
return max(st[l][k],st[r-(1<<k)+1][k]);
}
int main()
{
int i,j,k;
scanf("%d%d",&n,&w);
for(i=2;i<=1000000;i++)lg[i]=lg[i>>1]+1;
for(int kk=1;kk<=n;kk++)
{
scanf("%d",&m);
for(i=1;i<=m;i++)scanf("%d",&st[i][0]);
for(i=1;i<=20;i++)
for(j=1;j<=m;j++)
st[j][i]=max(st[j][i-1],st[j+(1<<(i-1))][i-1]);
if(2*m>=w)//第二种情况
{
for(i=1;i<=w;i++)
{
int l=m-w+i,r=i,v=query(max(l,1),min(r,m));
if(l<1||r>m)v=max(v,0);//可以有不选的情况(即靠近端点处的特殊情况)
ans[i]+=(ll)v;
ans[i+1]-=(ll)v;
}
} else {
int v1 = 0, v2 = 0;
for (i = 1; i <= m; i++)
{
v1 = max(v1, st[i][0]);
v2 = max(v2, st[m - i + 1][0]);
ans[i] += (ll) v1;
ans[i + 1] -= (ll) v1;
ans[w - i + 1] += (ll) v2;
ans[w - i + 2] -= (ll) v2;
}
ans[m + 1] += (ll) v1;
ans[w - m + 1] -= (ll) v1;
}
}
for(i=1;i<=w;i++)
{
ans[i]+=ans[i-1];
printf("%lld ",ans[i]);
}
puts("");
return 0;
}