bzoj 4540: [Hnoi2016]序列 莫队
题目:
给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
1,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。
题解:
首先这道题一看就是莫队.
然后就不会了...
校内胡测的第一题,可我不会啊...
只打了个暴力上去
首先这道题的难点在于拓展当前统计的答案区间.
也就是说:
在已经计算出来了区间\([l,r]\)的情况下,如何使其拓展到\([l,r+1]\)
首先我们知道枚举区间再计算区间的价值是不明智的
我们应该考虑枚举所有的元素,考虑其对区间的贡献.
在上面的拓展中,我们设\(left_i\)表示\(a_i\)前面第一个比它小的值的下标.
所以我们知道在拓展时新增的\((r+1) - l + 1\)个区间中
左端点在\([left_{r+1}+1,r+1]\)内的区间的价值一定都是\(a_{i+1}\)
所以现在我们的问题就是如何处理\([l,left_{r+1}]\)这段区间的贡献了.
我们设:\(f[i][j]\)表示处理区间\([i,j]\)所得到的贡献.
那么我们有:
\(f[i][j] = f[i][left_j] + (j - left_j)*a_{j}\)
我们发现实际上\(i\)只是限定了一个左端点而已.
所以我们查询区间\([l,r]\)时能够简单地取出\(f[l][r]\)
所以我们将\(f\)的第一个维度消去,即:
\(f[i] = f[left_i] + (i - left_i)*a_{i}\)
这样我们在查询区间\([l,r]\)时取出(f[r] - f[l-1])即可.
于是我们在莫队的时候再xjb乱搞一下就好了.
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline void read(ll &x){ x=0;char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; } const ll maxn = 210010; ll loger[maxn],minn[maxn][26],a[maxn]; ll minid[maxn][26]; inline void pre(ll n){ loger[1] = 0; for(ll i=2;i<=n;++i){ loger[i] = loger[i-1]; if( i == (1 << loger[i]+1)) ++ loger[i]; } for(ll i=n;i>=1;--i){ minn[i][0] = a[i]; minid[i][0] = i; for(ll j=1;i + (1<<j) - 1 <= n;++j){ minn[i][j] = min(minn[i][j-1],minn[i+(1<<j-1)][j-1]); if(minn[i][j] == minn[i][j-1]) minid[i][j] = minid[i][j-1]; if(minn[i][j] == minn[i+(1<<j-1)][j-1]) minid[i][j] = minid[i+(1<<j-1)][j-1]; } } } inline ll query(ll l,ll r){ ll k = loger[r-l+1]; if(minn[l][k] < minn[r-(1<<k)+1][k]) return minid[l][k]; return minid[r-(1<<k)+1][k]; } ll belong[maxn]; struct Node{ ll l,r,id; bool friend operator < (const Node &a,const Node &b){ if(belong[a.l] == belong[b.l]) return a.r < b.r; return belong[a.l] < belong[b.l]; } }q[maxn]; ll anss[maxn],fl[maxn],fr[maxn]; ll sta[maxn],top; inline void dp(ll n,ll *f){ sta[top = 1] = 0; for(ll i=1;i<=n;++i){ while(a[sta[top]] > a[i]) -- top; f[i] = (i - sta[top])*a[i] + f[sta[top]]; sta[++top] = i; } } inline ll upd_R(ll l,ll r){ ll p = query(l,r+1); return (p-l+1)*a[p] + fl[r+1] - fl[p]; } inline ll upd_L(ll l,ll r){ ll p = query(l-1,r); return (r-p+1)*a[p] + fr[l-1] - fr[p]; } int main(){ ll n,Q;read(n);read(Q);a[0] = -(1LL<<60); for(ll i=1;i<=n;++i) read(a[i]); pre(n);dp(n,fl);reverse(a+1,a+n+1);//puts("reversed."); dp(n,fr);reverse(a+1,a+n+1);reverse(fr+1,fr+n+1); ll block = sqrt(n) + 1; for(ll i=1;i<=n;++i) belong[i] = (i/block) + 1; for(ll i=1;i<=Q;++i){ read(q[i].l);read(q[i].r); q[i].id = i; }sort(q+1,q+Q+1); a[0] = 0; ll L = 1,R = 1,ans = a[1]; for(ll i=1;i<=Q;++i){ while(R < q[i].r) ans += upd_R(L,R++); while(R > q[i].r) ans -= upd_R(L,--R); while(L > q[i].l) ans += upd_L(L--,R); while(L < q[i].l) ans -= upd_L(++L,R); anss[q[i].id] = ans; } for(ll i=1;i<=Q;++i){ printf("%lld\n",anss[i]); } getchar();getchar(); return 0; }
- 【bzoj4540】【HNOI2016】【序列】【莫队+st表】
- BZOj 4540: [Hnoi2016]序列 [莫队 st表 预处理]
- [BZOJ4540][HNOI2016]序列 莫队
- BZOJ4540 [Hnoi2016]序列 【莫队 + ST表 + 单调栈】
- 【BZOJ4540】[Hnoi2016]序列 莫队算法+单调栈
- bzoj4540 [Hnoi2016]序列 (莫队+ST表+单调栈)
- [bzoj4540][Hnoi2016]序列 莫队+RMQ
- bzoj 4540: [Hnoi2016]序列 (莫队+ST表+单调栈|线段树)
- [BZOJ4540][Hnoi2016]序列(莫队/线段树)
- bzoj 4540: [Hnoi2016]序列 莫队算法+rmq
- bzoj 4540: [Hnoi2016]序列 莫队算法
- [莫队 单调栈] BZOJ 4540 [Hnoi2016]序列
- 【BZOJ4540】【HNOI2016】序列(莫队)
- [bzoj4540][Hnoi2016][序列] (莫队算法+单调栈+st表)
- 【BZOJ4540】【HNOI2016】序列 [莫队][RMQ]
- 【BZOJ4540】【HNOI2016】序列(莫队)
- [BZOJ4540][HNOI2016]序列(莫队)
- bzoj 4540 HNOI 2016 序列 莫队
- BZOJ 4540 [Hnoi2016]序列 | 莫队 详细题解
- bzoj 4540: [Hnoi2016]序列【单调栈+线段树】