P2048 [NOI2010] 超级钢琴
博客园还能导入题解?很神秘
题目描述
给出一个长度为 \(n\) 的序列,从中选出 \(k\) 个不同的二元组 \(\left(l,r\right),1 \le l \le r \le n\) ,使得 \(L \le r-l+1 \le R\) 且对于所有的 \(\sum_{l\le i\le r}a_i\) 的和最大。
思路
最坏情况下有 \(n^2\) 种不同的情况,排序选前 \(k\) 个复杂度 \(n^2logn+k\)。
考虑优化,发现取的个数 \(k\) 远远达不到 \(n^2\) ,看能不能走一步看一步,即每次选一个最大值,然后能快速找到下一个最大值。
考虑对于每个左端点 \(l\),从 \(\left(x,y\right)\) 里找到一个最优右端点 \(r\),即一个四元组 \(\left(l,x,y,r\right)\),从这 \(n\) 个里面选一个,一定是全局最优的,假设这个是 \(\left(l,x,y,R\right)\),那么接下来会分裂出两个四元组 \(\left(l,x,R-1,r\right)\) 和 \(\left(l,R+1,y,r\right)\),然后分别计算两边的最大值,直接用 \(\verb!st!\) 表预处即可,总复杂度 \(\left(n+k\right)log\left(n+k\right)\)。
\(\verb!oh!\) 忘说了,就是如何快速求出最优右端点,总所周知,区间和可以等于 \(sum_r-sum_{l-1}\),\(sum\) 是前缀和数组,那我固定了 \(l\),只需要找到 \(sum_r\) 最大的即可。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
#define getchar() (p1 == p2 && (p2 = (p1 = buf1) + fread(buf1, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf1[1 << 23], *p1 = buf1, *p2 = buf1, ubuf[1 << 23], *u = ubuf;
namespace IO
{
template<typename T>
void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}
template<typename T,typename... Args>
void read(T &_x,Args&...others){Read(_x);Read(others...);}
const int BUF=20000000;char buf[BUF],to,stk[32];int plen;
#define pc(x) buf[plen++]=x
#define flush(); fwrite(buf,1,plen,stdout),plen=0;
template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++to]=48+x%10;while(to) pc(stk[to--]);}
}
using namespace IO;
const int N = 2e6+10;//空间占用最大应为n+2*k,不要开小了
int n,k,o,L,R,a[N],sum[N],cnt,st[20][N],lg[N],x,ans;
priority_queue<pair<int,int> >p;//最大值和对应的四元组下标
inline int Max(int x,int y){if(sum[x] > sum[y]) return x; return y;}//范围sum最大的位置编号
struct w
{
int L,R,l,r;
}b[N];//四元组
signed main()
{
read(n),read(k),read(L),read(R);
for(int i = 1;i <= n;i++) read(a[i]),sum[i] = sum[i-1]+a[i],st[0][i] = i;
for(int i = 2;i <= n;i++) lg[i] = lg[i/2]+1;
for(int i = 1;i <= lg[n];i++)
for(int j = 1;j+(1<<i)-1 <= n;j++)
st[i][j] = Max(st[i-1][j],st[i-1][j+(1<<(i-1))]);
for(int i = 1;i+L-1 <= n;i++)
{
b[i].l = L+i-1,b[i].r = min(R+i-1,n);//R端点的取值范围
o = lg[b[i].r-b[i].l+1];
b[i].L = i,b[i].R = Max(st[o][b[i].l],st[o][b[i].r-(1<<o)+1]);
p.push(make_pair(sum[b[i].R]-sum[b[i].L-1],i)); cnt++;
}
for(int i = 1;i <= k;i++)
{
x = p.top().second; p.pop();
ans += sum[b[x].R]-sum[b[x].L-1]; //加入贡献
if(b[x].l != b[x].R)//不能分裂就不分
{
b[++cnt].L = b[x].L,b[cnt].l = b[x].l,b[cnt].r = b[x].R-1;
o = lg[b[cnt].r-b[cnt].l+1];
b[cnt].R = Max(st[o][b[cnt].l],st[o][b[cnt].r-(1<<o)+1]);
p.push(make_pair(sum[b[cnt].R]-sum[b[cnt].L-1],cnt));
}
if(b[x].R != b[x].r)
{
b[++cnt].L = b[x].L,b[cnt].l = b[x].R+1,b[cnt].r = b[x].r;
o = lg[b[cnt].r-b[cnt].l+1];
b[cnt].R = Max(st[o][b[cnt].l],st[o][b[cnt].r-(1<<o)+1]);
p.push(make_pair(sum[b[cnt].R]-sum[b[cnt].L-1],cnt));
}
}
print(ans); flush();
return 0;
}
浙公网安备 33010602011771号