4540: [Hnoi2016]序列
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 749 Solved: 374
[ Submit][ Status][ Discuss]
Description
给定长度为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。
Input
输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开
,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。
Output
对于每次询问,输出一行,代表询问的答案。
Sample Input
5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
28
17
11
11
17
17
11
11
17
HINT
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
Source
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
const int maxn = 1E5 + 10;
typedef long long LL;
struct Query{
int l,r,flag,num; Query(){}
Query(int l,int r,int flag,int num): l(l),r(r),flag(flag),num(num){}
bool operator < (const Query &b) const
{
if (flag < b.flag) return 1;
if (flag > b.flag) return 0;
return r < b.r;
}
}Q[maxn];
int n,m,Sqrt,lef = 1,rig,a[maxn],L[maxn],R[maxn],Min[maxn][20],pos[maxn][20],bin[maxn],len[maxn];
LL tot,ans[maxn],sl[maxn],sr[maxn];
stack <int> s;
void RMQ_pre()
{
for (int j = 1; j < 20; j++)
for (int i = 1; i <= n; i++)
{
int nex = i + (1<<(j-1));
if (nex > n) break;
if (Min[i][j-1] <= Min[nex][j-1])
Min[i][j] = Min[i][j-1],pos[i][j] = pos[i][j-1];
else Min[i][j] = Min[nex][j-1],pos[i][j] = pos[nex][j-1];
}
len[1] = 1;
for (int i = 2; i <= n; i++)
if ((len[i-1] << 1) < i)
bin[i] = bin[i-1] + 1,len[i] = (len[i-1] << 1);
else bin[i] = bin[i-1],len[i] = len[i-1];
}
int Getmin(int l,int r)
{
int le = r - l + 1,nex = r - len[le]+1;
if (Min[l][bin[le]] <= Min[nex][bin[le]]) return pos[l][bin[le]];
else return pos[nex][bin[le]];
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n >> m; Sqrt = sqrt(n);
for (int i = 1; i <= n; i++)
scanf("%d",&a[i]),Min[i][0] = a[i],pos[i][0] = i;
RMQ_pre();
for (int i = 1; i <= n; i++)
{
while (!s.empty() && a[s.top()] >= a[i])
R[s.top()] = i,s.pop();
s.push(i);
}
while (!s.empty()) R[s.top()] = n + 1,s.pop();
for (int i = n; i; i--)
{
while (!s.empty() && a[s.top()] > a[i])
L[s.top()] = i,s.pop();
s.push(i);
}
while (!s.empty()) L[s.top()] = 0,s.pop();
for (int i = 1; i <= n; i++) sl[i] = sl[L[i]] + 1LL*a[i]*(i - L[i]);
for (int i = n; i; i--) sr[i] = sr[R[i]] + 1LL*a[i]*(R[i] - i);
for (int i = 1; i <= m; i++)
{
int l,r; scanf("%d%d",&l,&r);
Q[i] = Query(l,r,(l%Sqrt == 0)?l/Sqrt:l/Sqrt+1,i);
}
sort(Q + 1,Q + m + 1);
for (int i = 1; i <= m; i++)
{
while (rig < Q[i].r)
{
++rig; int p = Getmin(lef,rig);
tot += 1LL*a[p]*(p-lef+1);
tot += (sl[rig] - sl[p]);
}
while (lef > Q[i].l)
{
--lef; int p = Getmin(lef,rig);
tot += 1LL*a[p]*(rig-p+1);
tot += (sr[lef] - sr[p]);
}
while (rig > Q[i].r)
{
int p = Getmin(lef,rig);
tot -= 1LL*a[p]*(p-lef+1);
tot -= (sl[rig] - sl[p]); --rig;
}
while (lef < Q[i].l)
{
int p = Getmin(lef,rig);
tot -= 1LL*a[p]*(rig-p+1);
tot -= (sr[lef] - sr[p]); ++lef;
}
ans[Q[i].num] = tot;
}
for (int i = 1; i <= m; i++) printf("%lld\n",ans[i]);
return 0;
}
此外,,本题还要线段树的解法,,参考bzoj4262