2019年11月14日更新ログ:
最近、私はこの問題に、このソリューションビットの腐った、更新見て、Shanfanjiujian、詳細に焦点を発見しました。より多くのコメントを支払うためのコード。その上江紫!
ステップの正のソリューション
私たちは、プレフィックスと素晴らしい学位をカウントし、RMQを初期化する必要があります。
サイクル\(I \)から(1 \)\に\(N- \) 、を出発点として、I 弦端でなければなりません(I + L - 1 \ \ ) に\(1 \ I + R - )との間ので、長い間隔RMQ内などで抽出し、超弦、および大放電素晴らしい小さな度の優先度キューに追加。
、スタック要素の上部を取り外し追加素晴らしいの度合い\(ANS \) 、および要素がから切断された(現在の要素の最後に現在の要素の左側境界--1)及び(\現在の要素右境界線の現在の要素の最後に+ 1(\))二つの部分、及び優先度キューを再び加え、順次に\(K \)倍。
その答えは、出力することができます
なぜプレフィックスとし、RMQを使うのか?
データ範囲は500,000、最適化するために、接頭辞とRMQを必要とする、ということは明らかです。
最も明白な接頭語の使用と蓄積のサイクルを最適化するために使用されると、\(N- \) 。\(RMQ \) 、対象の要件に沿って、最も明らか間隔値。
質問への答えのアルゴリズムはどちらもO(1)です。フロントとリアのマイナスプレフィックスは、RMQはその後、O(1)聞いて、それを初期化する必要があります。
なぜ第3のステップは、要素を切断し、プライオリティキューにすることですか?直接累積の前に\(K \)それはない要素?
まず第一に、我々は確かに決定することができます:優先順位キュー最初のメジャーコードでなければなりません\(グローバル\)最大和音。私が証明するか聞かないでください
そして、プライオリティキュー、二番目に大きい和音でなければなりません\(グローバル\)倍の和音は?これは必ずしも真実ではありません。
私たちは、各要素がグローバルランキングの要素サイズを取り出さなければならないことを確実にするための要素をカットし、プライオリティキューにする必要がありますので、
ラフ紙の上の計算に関する問題の考え方に、独自のソリューションと組み合わせて、独自のペンと紙、思い付くするには、理解します
それから私は、問題解決のアイデアが233を入れて置きます
問題は、民間の手紙I 233は本当にあります
ガイドラインのapple365アイデア@先生ありがとうございました。
ACコード
#include<bits/stdc++.h>
#include<cctype>
#pragma GCC optimize(2)
#define in(a) a = read()
#define out(a) write(a)
#define outn(a) out(a),putchar('\n')
#define ll long long
#define Min(a,b) a < b ? a : b
#define Max(a,b) a > b ? a : b
#define rg register
#define New ll
using namespace std;
namespace IO_Optimization{
inline New read()
{
New X = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch=getchar();
}
while(isdigit(ch))
{
X = (X << 3) + (X << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -X : X;
}
inline void write(New x)
{
if(x < 0) putchar('-'),x = -x;
if(x > 9) write(x/10);
putchar(x % 10 + '0');
}
#undef New
}
using namespace IO_Optimization;//上面一坨优化的东西不用在意
const int MAXN = 500000 + 2;//定义常亮
int n,k,L,R;
int sum[MAXN],lg[MAXN],dp[MAXN][20],pos[MAXN][20];
// 前缀和 lg2值
//dp[i][j]表示i的2^j次方祖先 pos数组来记录最佳位置
ll ans;
struct Node
{
int start, left, right, t, val;
//超级和弦的起点 左、右边界 最值位置 最值
bool operator < (const Node &next) const
{
return val < next.val; //最值从大到小排序
}
};
inline void RMQ_init() //预处理
{
for(rg int j = 1;j <= 20; ++j)
for(rg int i = 1;i + (1 << j) - 1 <= n; ++i)
{
if(dp[i][j - 1] > dp[i + (1 << (j - 1))][j - 1])
{
dp[i][j] = dp[i][j - 1];
pos[i][j] = pos[i][j - 1];//更新最值位置
}
else
{
dp[i][j] = dp[i + (1 << (j - 1))][j - 1];
pos[i][j] = pos[i + (1 << (j - 1))][j - 1];
}
}
return;
}
inline int RMQ_query(int l, int r) //返回最值的位置
{
int t, tmp = lg[r - l + 1];
if(dp[l][tmp] > dp[r - (1 << tmp) + 1][tmp])
t = pos[l][tmp];
else t = pos[r - (1 << tmp) + 1][tmp];
return t;
}
int main()
{
in(n),in(k),in(L),in(R);
lg[0] = -1;//lg2(0) = -1,方便后面预处理lg2值
for(rg int i = 1;i <= n; ++i)
{
int a = read();//读入音符
sum[i] = sum[i - 1] + a; //前缀和
lg[i] = lg[i >> 1] + 1; //预处理lg2值
dp[i][0] = sum[i];
pos[i][0] = i; //初始化最大值的位置
}
RMQ_init();//初始化
priority_queue<Node> pq; //定义优先队列
for(rg int i = 1;i + L - 1 <= n; ++i) //计算每个位置最大的超级和弦
{
int t = RMQ_query(i + L - 1, Min(n, i + R - 1));
Node cur;
cur.val = sum[t] - sum[i - 1]; //由前缀和取最大值
cur.t = t;
cur.start = i; //当前超级和弦的起始位置
cur.left = i + L - 1; //当前的左边界
cur.right = Min(n,i + R - 1); //当前的右边界
pq.push(cur); //入堆
}
for(rg int i = 1;i <= k; ++i) //取k次堆顶的值
{
Node cur = pq.top();
pq.pop();
ans = ans + cur.val; //累加结果
Node next;
if(cur.t > cur.left) //当前取最值的位置 大于 当前和弦的 左边界
{
next.start = cur.start;
next.left = cur.left;
next.right = cur.t - 1; //新的右边界
next.t = RMQ_query(next.left, next.right);
next.val = sum[next.t] - sum[next.start - 1];
pq.push(next);
}
if(cur.t < cur.right) //当前取最值的位置 小于 当前和弦的 右边界
{
next.start = cur.start;
next.left = cur.t + 1; //新的左边界
next.right = cur.right;
next.t = RMQ_query(next.left, next.right);
next.val = sum[next.t] - sum[next.start - 1];
pq.push(next);
}
}
outn(ans);
return 0;
}