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
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
39
4
13
16
题解Here!
我们发现要处理一堆区间$emmm...$
话说这题能不能用区间神器莫队呢?
好像不能欸,因为我没有想出来区间和值不好转变。
我们发现对于$p_1$类型的区间$[i,j]$。
如果$j>i$那么$j$一定是$i$右侧第一个比它大的点。
什么?$j<i$?,反转区间可以转化成情况一。
再考虑$p_2$情况类型的区间$[i,j]$
如果$i$小于$j$,则$i$一定是这样的点:它在区间$(p,q)$之间
$(p,q)$是开区间!!!
其中$[q.j]$是一个$p_1$类型的区间,$p$是$q$左侧第一个比q大的点。
同理$i>j$是可以反转区间转换为情况一。
通过反证法我们可以很容易的证明上面的说法可以不重不漏的
枚举出所有的$p_1,p_2$(假设有一个不符合条件的区间,发现很快就会出矛盾)。
发现上述一套花里胡哨的定义只需要两个信息:
点$i$左侧第一个比他大的点是谁;
点$i$右侧第一个比他大的点是谁。
这。。。当然单调栈$O(n)$预处理啊。。。
接下来是统计区间。
发现在线处理会直接晕掉,考虑离线处理。。。
把询问按右端点排序,之后每次拓展右端点。
更新所有合法的左端点即可。
然后我们发现$p_1$是单点修改,$p_2$是区间修改。
结果一堆大佬直接树状数组。。。
本蒟蒻表示根本不会。。。
于是直接上线段树。。。
然后线段树求一下就是结果了。
$BUT$!上述只处理了一半,即$i$小于$j$。
那另外一半呢?
我们可以把区间和询问统统反转,处理下一半情况。
细节看代码吧,不看代码刚才讲的好多细节没有。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define LSON rt<<1 #define RSON rt<<1|1 #define DATA(x) a[x].data #define SIGN(x) a[x].c #define LSIDE(x) a[x].l #define RSIDE(x) a[x].r #define WIDTH(x) (RSIDE(x)-LSIDE(x)+1) #define MAXN 200010 using namespace std; int n,m,top; int next[MAXN],stack[MAXN]; long long p1,p2,val[MAXN],ans[MAXN]; struct Sg_Tree{ long long data,c; int l,r; }a[MAXN<<2]; struct node{ int l,r,id; }que[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } bool cmp(const node &x,const node &y){ if(x.l==y.l)return x.r<y.r; return x.l<y.l; } inline void pushup(int rt){ DATA(rt)=DATA(LSON)+DATA(RSON); } inline void pushdown(int rt){ if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return; SIGN(LSON)+=SIGN(rt); DATA(LSON)+=SIGN(rt)*WIDTH(LSON); SIGN(RSON)+=SIGN(rt); DATA(RSON)+=SIGN(rt)*WIDTH(RSON); SIGN(rt)=0; } void buildtree(int l,int r,int rt){ int mid; LSIDE(rt)=l; RSIDE(rt)=r; SIGN(rt)=0; if(l==r){ DATA(rt)=0; return; } mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update(int l,int r,long long c,int rt){ int mid; if(l<=LSIDE(rt)&&RSIDE(rt)<=r){ SIGN(rt)+=c; DATA(rt)+=c*WIDTH(rt); return; } pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)update(l,r,c,LSON); if(mid<r)update(l,r,c,RSON); pushup(rt); } long long query(int l,int r,int rt){ int mid; long long ans=0; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt); pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans+=query(l,r,LSON); if(mid<r)ans+=query(l,r,RSON); return ans; } int half_find(int x){ int left=1,right=m+1,mid; while(left<=right){ mid=left+right>>1; if(que[mid].l<x)left=mid+1; else right=mid-1; } return left; } void work(){ top=0; stack[top]=n+1; buildtree(1,n+1,1); for(int i=n;i>=1;i--){ while(top&&val[stack[top]]<val[i])stack[top--]=0; next[i]=stack[top]; stack[++top]=i; } for(int i=n;i>=1;i--){ if(next[i]>i){ update(i+1,next[i],p2,1); update(next[i],next[i],p1-p2*2,1); } int pos=half_find(i); while(que[pos].l==i){ ans[que[pos].id]+=query(i+1,que[pos].r,1); pos++; } } } void init(){ n=read();m=read();p1=read();p2=read(); for(int i=1;i<=n;i++)val[i]=read(); for(int i=1;i<=m;i++){ que[i].l=read();que[i].r=read(); que[i].id=i; } sort(que+1,que+m+1,cmp); work(); for(int i=1;i<=n/2;i++)swap(val[i],val[n-i+1]); for(int i=1;i<=m;i++){ swap(que[i].l,que[i].r); que[i].l=n-que[i].l+1; que[i].r=n-que[i].r+1; } sort(que+1,que+m+1,cmp); work(); for(int i=1;i<=m;i++)printf("%lld\n",ans[i]); } int main(){ init(); return 0; }