1. 程式人生 > >【題解】HNOI2016序列

【題解】HNOI2016序列

span 指針 min class div 有用 for 由於 ||

  也想了有半天,沒有做出來……實際上做法確實也是十分精妙的。這裏推薦一個blog,個人認為這位博主講得挺好了:Sengxian‘s Blog;

  感覺啟示是:首先要加強對莫隊算法 & ST表的熟練程度。 在想與數列有關的問題的時候,要盡量多在草稿紙上手玩幾組數據,觀察其中的聯系,應該可以觀察到許多有用的性質。

  不過這題還有一個地方:網上的題解基本上都是先移動了右指針,後移動左指針;如果反過來,竟然被卡到只有10分。具體的原因我也不知道,但我猜想是不是由於r 和 l 的左右顛倒導致程序出現了一些問題,所以特判了一下,保證左指針永遠在右指針的左側。

#include <bits/stdc++.h>
using namespace std;
#define maxn 200000
#define int long long
int n, q, B = 316, a[maxn];
int ans, Ans[maxn];
int top, S[maxn], Log[maxn];
int LS[maxn], RS[maxn];

struct ques
{
int l, r, id, bel;
ques(int L = 0, int R = 0, int idx = 0)
{ l = L, r = R, id = idx, bel = (l / B) + 1; }
bool operator < (const ques &q)
const{
return (bel < q.bel || (bel == q.bel && r < q.r));
}
}Q[maxn];

int read()
{
int x = 0, k = 1;
char c;
c = getchar();
while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); }
while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
return x * k;
}

namespace RMQ
{
int ST[maxn][20];
int cmp(const int &i, const int &j) { return a[i] < a[j] ? i : j; }
void Work()
{
for(int i = 2; i <= n; i ++) Log[i] = Log[i >> 1] + 1;
for(int i = 1; i <= n; i ++) ST[i][0] = i;
for(int j = 1, k = 1 << j; k <= n; j ++, k = 1 << j)
for(int i = 1; k + i - 1 <= n; i ++)
ST[i][j] = cmp(ST[i][j - 1], ST[i + (k >> 1)][j - 1]);
}

int Get_min(int l, int r)
{
int k = Log[r - l + 1];
return cmp(ST[l][k], ST[r - (1 << k) + 1][k]);
}
}

void Get_Sum(int *LS)
{
S[top = 0] = 0;
for(int i = 1; i <= n; i ++)
{
while(top && a[S[top]] >= a[i]) top --;
LS[i] = LS[S[top]] + (i - S[top]) * a[i];
S[++ top] = i;
}
}

int Go_Right(int l, int r)
{
int pos = RMQ :: Get_min(l, r);
return (pos - l + 1) * a[pos] + LS[r] - LS[pos];
}

int Go_Left(int l, int r)
{
int pos = RMQ :: Get_min(l, r);
return (r - pos + 1) * a[pos] + RS[l] - RS[pos];
}

signed main()
{
n = read(), q = read(), B = sqrt(n) + 1;
for(int i = 1; i <= n; i ++) a[i] = read();
RMQ :: Work();
for(int i = 1; i <= q; i ++)
{
int x = read(), y = read();
Q[i] = ques(x, y, i);
}
sort(Q + 1, Q + 1 + q);
a[0] = -2e9; Get_Sum(LS); reverse(a + 1, a + 1 + n);
Get_Sum(RS); reverse(a + 1, a + 1 + n);
reverse(RS + 1, RS + 1 + n);
int l = 1, r = 1; ans = a[1];
for(int i = 1; i <= q; i ++)
{
if(Q[i].l > r)
{
while(r < Q[i].r) ans += Go_Right(l, r + 1), r ++;
while(r > Q[i].r) ans -= Go_Right(l, r), r --;
while(l < Q[i].l) ans -= Go_Left(l, r), l ++;
while(l > Q[i].l) ans += Go_Left(l - 1, r), l --;
}
else
{
while(l < Q[i].l) ans -= Go_Left(l, r), l ++;
while(l > Q[i].l) ans += Go_Left(l - 1, r), l --;
while(r < Q[i].r) ans += Go_Right(l, r + 1), r ++;
while(r > Q[i].r) ans -= Go_Right(l, r), r --;
}
Ans[Q[i].id] = ans;
}
for(int i = 1; i <= q; i ++) printf("%lld\n", Ans[i]);
return 0;
}

【題解】HNOI2016序列