zoukankan      html  css  js  c++  java
  • [AH2017/HNOI2017]影魔

    题目

    好题啊

    先把两种贡献翻译成人话

    对于((i,j)),如果(a_i,a_j)恰好是([i,j])之间的最大值和次大值,那么((i,j))产生(p1)的贡献

    否则对于((i,j))(a_i)是最大值或者(a_j)是最大值,那么就产生(p2)的贡献

    哎这个一个最大值一个严格次大值好像有点眼熟啊

    这不bzoj原题

    根据那道题我们发现的性质,能产生(p1)贡献的点对不会超过(2n)个,于是我们用单调栈将这些点对都预处理出来,之后树状数组+扫描线就可以知道每个询问有多少个(p1)的贡献

    现在求出(p2)的贡献就好了

    我们考虑一个元素往左右两边扩展,扩展到比它大的元素就停止,这些扫到的点显然都会产生贡献,但是这个点对产生的贡献是(p1)还是(p2)就不好算了

    但是我们都已经算出来(p1)的点对数量了,只要拿总数量减一下就是(p2)点对的数量了

    显然每一个位置往左往右扩展到哪里单调栈已经帮我们求好了,所以现在的问题变成了求每一个询问区间内,每一个位置在区间内往左往右扩展的总长度与之和

    考虑把这个扩展的总长度变成一个区间加法,于是我们可以把每一个元素的扩展搞成一个三元组((l,r,k)),扩展到([l,r]),扩展的位置是(k)

    我们把每个询问拆成两个,之后将这些区间按照(k)来排序,线段树+扫描线就可以了

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define lowbit(x) ((x)&(-x))
    #define LL long long
    #define re register
    #define maxn 200005
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    struct Seg{int x,y,k;}t[maxn<<1];
    struct node{int x,y;}p[maxn<<1];
    struct Ask{int l,r,rk;}q[maxn];
    struct _Ask{int x,rk,o,l,r;}_q[maxn<<1];
    int n,m,p1,p2,top,tot;
    int l[maxn],r[maxn],a[maxn],st[maxn],c[maxn];
    int ls[maxn<<2],rs[maxn<<2],tag[maxn<<2];LL d[maxn<<2];
    int Ans1[maxn];LL Ans2[maxn];
    inline int cxp(_Ask A,_Ask B) {return A.x<B.x;}
    inline int cmp(Ask A,Ask B) {return A.r<B.r;}
    inline int cop(node A,node B) {return A.y<B.y;}
    inline void add(int x,int val) {for(re int i=x;i<=n;i+=lowbit(i)) c[i]+=val;}
    inline int ask(int x) {int cnt=0;for(re int i=x;i;i-=lowbit(i)) cnt+=c[i];return cnt;}
    void build(int x,int y,int i) {
    	ls[i]=x,rs[i]=y;
    	if(x==y) return;
    	int mid=x+y>>1;
    	build(x,mid,i<<1),build(mid+1,y,i<<1|1);
    }
    inline void pushdown(int i) {
    	if(!tag[i]) return;
    	tag[i<<1]+=tag[i],tag[i<<1|1]+=tag[i];
    	d[i<<1]+=tag[i]*(rs[i<<1]-ls[i<<1]+1);
    	d[i<<1|1]+=tag[i]*(rs[i<<1|1]-ls[i<<1|1]+1);
    	tag[i]=0;
    }
    void change(int x,int y,int i) {
    	if(x<=ls[i]&&y>=rs[i]) {
    		tag[i]++;
    		d[i]+=rs[i]-ls[i]+1;
    		return;
    	}
    	pushdown(i);
    	int mid=ls[i]+rs[i]>>1;
    	if(y<=mid) change(x,y,i<<1);
    		else if(x>mid) change(x,y,i<<1|1);
    			else change(x,y,i<<1|1),change(x,y,i<<1);
    	d[i]=d[i<<1|1]+d[i<<1];
    }
    int query(int x,int y,int i) {
    	if(x<=ls[i]&&y>=rs[i]) return d[i];
    	pushdown(i);
    	int mid=ls[i]+rs[i]>>1;
    	if(y<=mid) return query(x,y,i<<1);
    	if(x>mid) return query(x,y,i<<1|1);
    	return query(x,y,i<<1|1)+query(x,y,i<<1);
    }
    signed main() {
    	n=read(),m=read();p1=read(),p2=read();
    	for(re int i=1;i<=n;i++) a[i]=read();
    	for(re int i=1;i<=n;i++) {
    		while(top&&a[st[top]]<a[i]) r[st[top--]]=i;
    		st[++top]=i;
    	}
    	while(top) r[st[top--]]=n+1;
    	for(re int i=n;i;--i) {
    		while(top&&a[st[top]]<a[i]) l[st[top--]]=i;
    		st[++top]=i;
    	}
    	while(top) l[st[top--]]=0;
    	for(re int i=1;i<=n;i++) {
    		if(l[i]) p[++tot]=(node){l[i],i};
    		if(r[i]!=n+1) p[++tot]=(node){i,r[i]};
    	}
    	for(re int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].rk=i;
    	std::sort(p+1,p+tot+1,cop),std::sort(q+1,q+m+1,cmp);
    	top=1;int now=1;
    	for(re int i=1;i<=n;i++) {
    		while(now<=tot&&p[now].y<=i) add(p[now].x,1),now++;
    		while(top<=m&&q[top].r<=i) 
    			Ans1[q[top].rk]=ask(q[top].r)-ask(q[top].l-1),top++;
    	}
    	tot=0;now=0;build(1,n,1);
    	for(re int i=1;i<=n;i++) {
    		if(l[i]+1<=i-1) t[++tot]=(Seg){l[i]+1,i-1,i};
    		if(i+1<=r[i]-1)	t[++tot]=(Seg){i+1,r[i]-1,i};
    	}
    	for(re int i=1;i<=m;i++) 
    		_q[++now]=(_Ask){q[i].l-1,q[i].rk,-1,q[i].l,q[i].r},_q[++now]=(_Ask){q[i].r,q[i].rk,1,q[i].l,q[i].r};
    	std::sort(_q+1,_q+now+1,cxp);
    	int tmp=1,cnt=1;
    	for(re int i=0;i<=n;i++) {
    		while(tmp<=tot&&t[tmp].k<=i) change(t[tmp].x,t[tmp].y,1),tmp++;
    		while(cnt<=now&&_q[cnt].x<=i) Ans2[_q[cnt].rk]+=_q[cnt].o*query(_q[cnt].l,_q[cnt].r,1),cnt++;
    	}
    	for(re int i=1;i<=m;i++) 
    		printf("%lld
    ",(LL)p1*Ans1[i]+(LL)p2*(Ans2[i]-Ans1[i]));
    	return 0;
    }
    
  • 相关阅读:
    极具创意的专辑封面
    【Linux必知必会】五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)
    【Ubuntu技巧】Ubuntu下gedit 打开txt文件乱码的处理方法
    【Linux原理】Linux中硬链接和软链接的区别和联系
    【短语学习】out of the box的含义和翻译
    【Ubuntu技巧】在全新安装的Ubuntu上快速重装软件包
    【论文阅读心得】图像识别中一个常用词的中英文释义——artifact
    【短语学习】狮子那一份the lions share
    【OpenCV学习】摄像头显示、录像、拍照程序
    【Perl学习】学习笔记(持续更新中)
  • 原文地址:https://www.cnblogs.com/asuldb/p/10453033.html
Copyright © 2011-2022 走看看