4540: [Hnoi2016]序列

4540: [Hnoi2016]序列

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 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

Sample Output

28
17
11
11
17

HINT

1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9

Source

[ Submit][ Status][ Discuss]

对于不同询问中区间端点的移动,只新增或减少以左端点为头或以右端点结束的区间
那么,,,可以使用莫队维护
假设我们现在已经处理好[l,r]的答案了,考虑需要得到[l,r+1]的情况
假设[l,r]中最小值位置为p,且a[p] < a[r+1]
预处理sum[i]:以i结尾的所有区间中最小值之和
L[i]:位置i往左,第一个小于i的数字的位置
那么,sum[i] = sum[L[i]] + a[i]*(i - L[i])
对于所有区间[k,r+1] (l <= k <= p),答案是a[p],直接计入ans
对于所有区间[k,r+1] (p < k <= p),可以用sum数组统计,sum[r+1] - sum[p]
显然满足区间可减性,,不证明了。。。。
除了L[i],还要维护R[i]:i右侧第一个小于等于i的位置
单调栈预处理即可
#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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值