BZOJ4826: [Hnoi2017]影魔

Description

影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。千百年来,他收集了各式各样
的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。每一个灵魂,都有着自己的战斗力,而影魔,靠
这些战斗力提升自己的攻击。奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。
第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i,j(i<j)来说,若不存在 k[s](i
<s<j)大于 k[i]或者 k[j],则会为影魔提供 p1 的攻击力(可理解为:当 j=i+1 时,因为不存在满足 i<s<j 的 s,从
而 k[s]不存在,这时提供 p1 的攻击力;当 j>i+1 时,若max{k[s]|i<s<j}<=min{k[i],k[j]} , 则 提 供 p1 的 攻
 击 力 ); 另 一 种 情 况 , 令 c 为k[i+1],k[i+2],k[i+3]......k[j-1]的最大值,若 c 满足:k[i]<c<k[j],或
者 k[j]<c<k[i],则会为影魔提供 p2 的攻击力,当这样的 c 不存在时,自然不会提供这 p2 的攻击力;其他情况的
点对,均不会为影魔提供攻击力。影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任
意一段区间[a,b],1<=a<b<=n,位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑 所有满足a<=i<j<=b 的灵
魂对 i,j 提供的攻击力之和。顺带一提,灵魂的战斗力组成一个 1 到 n 的排列:k[1],k[2],...,k[n]。

Input

第一行 n,m,p1,p2
第二行 n 个数:k[1],k[2],...,k[n]
接下来 m 行,每行两个数 a,b,表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。
1 <= n,m <= 200000;1 <= p1,p2 <= 1000

Output

共输出 m 行,每行一个答案,依次对应 m 个询问。

Sample Input

10 5 2 3
7 9 5 1 3 10 6 8 2 4
1 7
1 9
1 3
5 9
1 5

Sample Output

30
39
4
13
16

HINT

Source

主席树
首先第一种区间只有O(N)个,直接提取出来即可
第二种在两个值之间不好统计,可以考虑用<一边的减去第一种
然后推一推式子发现是个二维数点,维护3棵主席树即可
#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define fill( x, y ) memset( x, y, sizeof x )
#define copy( x, y ) memcpy( x, y, sizeof x )
using namespace std;

typedef long long LL;
typedef pair < int, int > pa;

const int MAXN = 200020;

inline int read()
{
	int sc = 0; char ch = getchar();
	while( ch < '0' || ch > '9' ) ch = getchar();
	while( ch >= '0' && ch <= '9' ) sc = sc * 10 + ch - '0', ch = getchar();
	return sc;
}

int n, Q, p1, p2, a[MAXN], st[MAXN], top, tot;

struct Tree
{
	int a[MAXN], mx[MAXN << 2], tot, L[MAXN], root[MAXN];

	struct node
	{
		int ls, rs, cnt;
		LL v1, v2;
	}e[MAXN * 25];

	inline void build(int x, int l, int r)
	{
		if( l == r ) { mx[ x ] = a[ l ]; return ; }
		int mid = l + r >> 1;
		build( x << 1, l, mid ); build( x << 1 | 1, mid + 1, r ); mx[ x ] = max( mx[ x << 1 ], mx[ x << 1 | 1 ] );
	}

	inline int querypos(int x, int l, int r, int p, int v)
	{
		if( mx[ x ] <= v ) return 0;
		if( l == r ) return l;
		int mid = l + r >> 1, ret;
		if( mid <= p && ( ret = querypos( x << 1 | 1, mid + 1, r, p, v ) ) ) return ret;
		return querypos( x << 1, l, mid, p, v );
	}

	inline void insert(int &x, int y, int l, int r, int p, int v1, int v2)
	{
		e[ x = ++tot ] = e[ y ];
		e[ x ].v1 += v1; e[ x ].v2 += v2; e[ x ].cnt++;
		if( l == r ) return ;
		int mid = l + r >> 1;
		if( p <= mid ) insert( e[ x ].ls, e[ y ].ls, l, mid, p, v1, v2 );
		else insert( e[ x ].rs, e[ y ].rs, mid + 1, r, p, v1, v2 );
	}

	inline void solve()
	{
		build( 1, 1, n );
		for( int i = 1 ; i <= n ; i++ )
		{
			L[ i ] = querypos( 1, 1, n, i - 1, a[ i ] ) + 1;
			insert( root[ i ], root[ i - 1 ], 1, n, L[ i ], i, L[ i ] );
		}
	}

	inline LL query(int x, int l, int r, int p)
	{
		if( !x ) return 0;
		if( r <= p ) return e[ x ].v1 - 1LL * p * e[ x ].cnt;
		if( l > p ) return e[ x ].v1 - e[ x ].v2;
		int mid = l + r >> 1;
		return query( e[ x ].ls, l, mid, p ) + query( e[ x ].rs, mid + 1, r, p );
	}
}t1, t2;

struct info { int l, r; bool operator < ( const info &b ) const { return l < b.l; } } range[MAXN];
struct node { int ls, rs, v; } e[MAXN * 25];
int root[MAXN];

inline void insert(int &x, int y, int l, int r, int p)
{
	e[ x = ++tot ] = e[ y ]; e[ x ].v++;
	if( l == r ) return ;
	int mid = l + r >> 1;
	if( p <= mid ) insert( e[ x ].ls, e[ y ].ls, l, mid, p );
	else insert( e[ x ].rs, e[ y ].rs, mid + 1, r, p );
}

inline int query(int x, int l, int r, int p)
{
	if( !x || l >= p ) return 0;
	if( r < p ) return e[ x ].v;
	int mid = l + r >> 1;
	return query( e[ x ].ls, l, mid, p ) + query( e[ x ].rs, mid + 1, r, p );
}

int main()
{
#ifdef wxh010910
	freopen( "data.in", "r", stdin );
#endif
	n = read(), Q = read(), p1 = read(), p2 = read();
	for( int i = 1 ; i <= n ; i++ ) a[ i ] = read(), t1.a[ i ] = t2.a[ n - i + 1 ] = a[ i ];
	t1.solve(); t2.solve();
	for( int i = 1 ; i <= n ; i++ )
	{
		while( top && a[ st[ top ] ] < a[ i ] )
			range[ st[ top-- ] ].r = i - 1;
		st[ ++top ] = i;
	}
	while( top ) range[ st[ top-- ] ].r = n;
	for( int i = n ; i ; i-- )
	{
		while( top && a[ st[ top ] ] < a[ i ] )
			range[ st[ top-- ] ].l = i + 1;
		st[ ++top ] = i;
	}
	while( top ) range[ st[ top-- ] ].l = 1;
	sort( range + 1, range + n + 1 );
	for( int i = n ; i ; i-- ) insert( root[ i ], root[ i + 1 ], 1, n, range[ i ].r );
	while( Q-- )
	{
		int l = read(), r = read(), cnt = 0, L = 1, R = n, pos = n + 1;
		LL cur = t1.query( t1.root[ r ], 1, n, l ) - t1.query( t1.root[ l - 1 ], 1, n, l );
		l = n - l + 1, r = n - r + 1; swap( l, r );
		cur += t2.query( t2.root[ r ], 1, n, l ) - t2.query( t2.root[ l - 1 ], 1, n, l );
		cur *= p2;
		l = n - l + 1, r = n - r + 1; swap( l, r );
		while( L <= R )
		{
			int mid = L + R >> 1;
			if( range[ mid ].l > l ) pos = mid, R = mid - 1;
			else L = mid + 1;	
		}
		cnt = r - l + query( root[ pos ], 1, n, r );
		printf( "%lld\n", cur + 1LL * ( p1 - p2 ) * cnt );
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值