首页 > 代码库 > [HNOI2017]影魔

[HNOI2017]影魔

题目背景

影魔,奈文摩尔,据说有着一个诗人的灵魂。 事实上,他吞噬的诗人灵魂早已成千上万。

千百年来,他收集了各式各样的灵魂,包括诗人、 牧师、 帝王、 乞丐、 奴隶、 罪人,当然,还有英雄。

题目描述

每一个灵魂,都有着自己的战斗力,而影魔,靠这些战斗力提升自己的攻击。

奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i, j(i<j)来说,若不存在 ks大于 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]。

输入输出格式

输入格式:

 

输入文件名为 sf.in。

第一行 n,m,p1,p2

第二行 n 个数: k[1],k[2],…,k[n]

接下来 m 行, 每行两个数 a,b, 表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。

 

输出格式:

 

输出文件名为 sf.out

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

 

输入输出样例

输入样例#1:
10 5 2 3
7 9 5 1 3 10 6 8 2 4
1 7
1 9
1 3
5 9
1 5
输出样例#1:
30
39
4
13
16

说明

30%: 1<= n,m <= 500。

另 30%: p1=2*p2。

100%:1 <= n,m <= 200000; 1 <= p1,p2 <= 1000。

 

<style>pre.cjk { font-family: "Droid Sans Fallback" } p { margin-bottom: 0.25cm; line-height: 120% }</style>
感觉这道题算不上数据结构题,这是一道思维题…
要统计每个点作为左端点和右端点的贡献,先考虑左端点.
求出每个点向右的第一个比他大的位置,记为R[i],这个可以用一个单调栈来求.
然后从右往左扫描每个点.
假设现在扫到了点i,那么右端点在R[i]以后的显然是没有贡献的(可能会有贡献,比如后面有一个点比R[i],但是这个区间的贡献会后面在枚举右端点的时候算到,这里不重复算.)
那么右端点在[i+1,R[i]]这个区间内的点是可能有贡献的,就先假如都有p2的贡献,那么把这个区间加上p2.
考虑到有些点并没有p2的贡献,那么他就会有p1的贡献.这时我们知道R[i]这个点会有p1的贡献,但是他被当做p2统计了两次,那么就在R[i]+p1-2*p2.其他有p1贡献的点,也会被算到.
然后把i的贡献算完之后,计算左端点为i的询问:[1,询问右端点]的和.
这个很难想到啊,但是第二档部分分好像就是对这个的提示...考试的时候还一直不晓得这是什么套路…
然后右端点的贡献就把序列和询问区间都翻转一下然后做相同的事情就可以了.


 1 #include<bits/stdc++.h>
 2 #define ls o*2
 3 #define rs o*2+1
 4 #define mi int mid=(l+r)>>1
 5 #define maxn 200010
 6 #define LL long long
 7 using namespace std;
 8 struct data{int l,r,id;}q[maxn];
 9 int n,m,p1,p2,a[maxn];
10 LL b[maxn*4],size[maxn*4],lazy[maxn*4],ans[maxn];
11 int R[maxn];
12 inline bool cmp(const data &A,const data &B){
13   return A.l<B.l;
14 }
15 inline void down(int o){
16   if(!lazy[o]) return;
17   int k=lazy[o];lazy[o]=0;
18   lazy[ls]+=k,lazy[rs]+=k;
19   b[ls]+=k*size[ls],b[rs]+=k*size[rs];
20 }
21 void build(int o,int l,int r){
22   lazy[o]=0;
23   if(l==r){b[o]=0,size[o]=1;return;}
24   mi;
25   build(ls,l,mid),build(rs,mid+1,r);
26   b[o]=b[ls]+b[rs];
27   size[o]=size[ls]+size[rs];
28 }
29 void add(int o,int l,int r,int u,int v,int w){
30   if(l!=r) down(o);
31   if(l>=u && r<=v){b[o]+=size[o]*w;lazy[o]+=w;return;}
32   if(l>v || r<u)return;
33   mi;
34   if(v<=mid) add(ls,l,mid,u,v,w);
35   else if(u>mid) add(rs,mid+1,r,u,v,w);
36   else add(ls,l,mid,u,mid,w),add(rs,mid+1,r,mid+1,v,w);
37   b[o]=b[ls]+b[rs];
38 }
39 LL query(int o,int l,int r,int u,int v){
40   if(l!=r) down(o);
41   if(l>=u && r<=v) return b[o];
42   if(l>v || r<u)return 0;
43   mi;
44   if(v<=mid) return query(ls,l,mid,u,v);
45   else if(u>mid) return query(rs,mid+1,r,u,v);
46   else return query(ls,l,mid,u,mid)+query(rs,mid+1,r,mid+1,v);
47 }
48 inline void calc(){
49   build(1,1,n+1);
50   sort(q+1,q+m+1,cmp);a[n+1]=n+1;
51   stack<int>s;
52   s.push(1);
53   for(int i=2;i<=n+1;i++){
54     while(!s.empty() && a[i]>a[s.top()]) R[s.top()]=i,s.pop();
55     s.push(i);
56   }
57   int j=m;
58   for(int i=n;i;i--){
59     add(1,1,n+1,i+1,R[i],p2),add(1,1,n+1,R[i],R[i],p1-2*p2);
60     while(j && q[j].l==i) ans[q[j].id]+=query(1,1,n+1,1,q[j].r),j--;
61   }
62 }
63 int main(){
64   freopen("!.in","r",stdin);
65   freopen("!.out","w",stdout);
66   scanf("%d%d%d%d",&n,&m,&p1,&p2);
67   for(int i=1;i<=n;i++)
68     scanf("%d",&a[i]);
69   for(int i=1;i<=m;i++)
70     scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
71   calc();
72   reverse(a+1,a+n+1);
73   for(int i=1;i<=m;i++) q[i].l=n+1-q[i].l,q[i].r=n+1-q[i].r,swap(q[i].l,q[i].r);
74   calc();
75   for(int i=1;i<=m;i++)
76     printf("%lld\n",ans[i]);
77   return 0;
78 }

 

[HNOI2017]影魔