BZOJ4826 HNOI2017 影魔

传送门

题目大意

给定一个长为$N$的排列$A$,给定$p_1,p_2$,对于点对$i,j(i<j)$当$i+1=j$或$\forall k(i<k<j)$不存在$A_k<\min\{A_i,A_j\}$,则这一点对对答案的贡献是$p_1$,若$\min\{A_i,A_j\}<\max\{A_k\}(i<k<j)<\max\{A_i,A_j\}$。那么这一点对对答案的贡献是$p_2$。

一共有$m$组询问,每次询问给定区间$[l,r]$求仅考虑$l\leq i< j\leq r$的点对,求其贡献和。

 

题解

点对对答案的贡献两种,一种是$(i,i+1)$,每一个询问可以$O(1)$算,就先不管它。

另一种$(i,j)(j>i+1)$,一定存在唯一$k$使得$A_k$是$A_{[i+1,j-1]}$中最大的。

考虑枚举这个$k$,分别找到左右侧距离$k$最近的大于$A_k$的

如果$i,j$均找不到,那么无贡献,无需考虑。

如果$\min\{A_i,A_j\}<\max\{A_k\}(i<k<j)<\max\{A_i,A_j\}$,如果$i$出现在询问$[l,r]$中,那么一定有$p_2\times|[l,r]\cap[k+1,j-1]|$的贡献

同理,如果$j$出现在询问$[l,r]$中,那么一定有$p_2\times|[l,r]\cap[i+1,k-1]|$的贡献。

如果$i,j$均出现在$[l,r]$中,那么这个选$i,j$还会有额外$p_1$的贡献,这个可以等价于如果$i$出现在$[l,r]$中那么一定会有$p_1\times|[l,r]\cap[r,r]|$的贡献。

这样一来每一组点对$i,j$,它的贡献一定恰好会被$\max\{A_k\}(i<k<j)$统计到,对于一个一组询问只需要求$[l,r]$包含的点对区间$[l,r]$产生的贡献之和即可。

可以先用单调栈预处理每一个$k$的$i,j$,把一次形如 如果$pos$在区间$[l,r]$中那么对$[tl,tr]$每个位置$+num$看作一次修改。

对于每一个询问$[l,r]$,把它看做$pos\in[1,l-1]$中$[l,r]$的区间和与$pos\in[1,r]$中$[l,r]$的区间和之差,再加上$(i,i+1)$的贡献。

 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 200020
using namespace std;
namespace IO{
	const int BS=(1<<21)+5; int Top=0;
	char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1;
	char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;}
	void flush(){fwrite(OT,1,OS-OT,stdout);}
	void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;}
	void write(LL x){
		if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-');
		while(x) SS[++Top]=x%10,x/=10;
		while(Top) Putchar(SS[Top]+'0'),--Top;
	}
	int read(){
		int nm=0,fh=1; char cw=Getchar();
		for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh;
		for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0');
		return nm*fh;
	}
}
using namespace IO;
int n,m,T,m1,m2,p[M],K;
int S[M],top,od[M],cnt,rt[M],last[M],nxt[M],tot[M<<2];
LL sum[M<<2],ans[M];
struct mdf{int ps,LS,RS,num;}d[M<<2];
struct qu{int pos,fh,id,tl,tr;}t[M<<1];
bool cmp(mdf i,mdf j){return i.ps<j.ps;}
bool cmpt(qu i,qu j){return i.pos<j.pos;}
#define calc(l1,r1,l2,r2) (min(r1,r2)-max(l1,l2)+1)
void add(int x,int l,int r,int ls,int rs,int dt){
	if(ls<=l&&r<=rs){tot[x]+=dt;return;} int mid=((l+r)>>1);
	sum[x]+=(LL)calc(l,r,ls,rs)*(LL)dt;
	if(ls<=mid) add(x<<1,l,mid,ls,rs,dt);
	if(rs>mid) add(x<<1|1,mid+1,r,ls,rs,dt);
}
LL qry(int x,int l,int r,int ls,int rs){
	LL res=(LL)calc(l,r,ls,rs)*(LL)tot[x];
	if(ls<=l&&r<=rs){return res+sum[x];} int mid=((l+r)>>1);
	if(ls<=mid) res+=qry(x<<1,l,mid,ls,rs);
	if(rs>mid) res+=qry(x<<1|1,mid+1,r,ls,rs); return res;
}
int main(){
	freopen("c.in","r",stdin);
	n=read(),T=read(),m1=read(),m2=read();
	for(int i=1;i<=n;i++) p[i]=read(),od[i]=i;
	for(int i=1;i<=n;i++){
		while(top&&p[S[top]]<p[i]) top--; last[i]=S[top],S[++top]=i;
	} top=0;
	for(int i=n;i;--i){
		while(top&&p[S[top]]<p[i]) top--; nxt[i]=S[top],S[++top]=i;
		if(!nxt[i]) nxt[i]=n+1;
	}
	for(int i=1;i<=n;i++){
		if(last[i]&&i+1<nxt[i]) d[++m]=mdf{last[i],i+1,nxt[i]-1,m2};
		if(nxt[i]<=n&&i-1>last[i]) d[++m]=mdf{nxt[i],last[i]+1,i-1,m2};
		if(last[i]>0&&nxt[i]<=n) d[++m]=mdf{nxt[i],last[i],last[i],m1};
	} sort(d+1,d+m+1,cmp);
	for(int i=1;i<=T;i++){
		int t1=read(),t2=read();
		if(t1>1) t[++K]=qu{t1-1,-1,i,t1,t2};
		t[++K]=qu{t2,1,i,t1,t2},ans[i]=(LL)m1*(LL)(t2-t1);
	} sort(t+1,t+K+1,cmpt);
	for(int i=1,nw=1;i<=K;i++){
		while(nw<=m&&d[nw].ps<=t[i].pos) add(1,1,n,d[nw].LS,d[nw].RS,d[nw].num),nw++;
		ans[t[i].id]+=(LL)t[i].fh*qry(1,1,n,t[i].tl,t[i].tr);
	}
	for(int i=1;i<=T;i++) write(ans[i]),Putchar('\n');
	flush(); return 0;
}

 

当然这道题也有在线做法,你可以用大量的时间和空间使用主席树区间修改标记永久化来做到。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 200020
using namespace std;
namespace IO{
    const int BS=(1<<21)+5; int Top=0;
    char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1;
    char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;}
    void flush(){fwrite(OT,1,OS-OT,stdout);}
    void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;}
    void write(LL x){
        if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-');
        while(x) SS[++Top]=x%10,x/=10;
        while(Top) Putchar(SS[Top]+'0'),--Top;
    }
    int read(){
        int nm=0,fh=1; char cw=Getchar();
        for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh;
        for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0');
        return nm*fh;
    }
}
using namespace IO;
int n,m,T,m1,m2,p[M],L[M*100],R[M*100];
int S[M],top,od[M],cnt,rt[M],last[M],nxt[M];
LL sum[M*100],tot[M*100];
struct qs{
    int ps,LS,RS,num;
    qs(){} qs(int _ps,int _LS,int _RS,int _num){ps=_ps,LS=_LS,RS=_RS,num=_num;}
}q[M<<2];
bool cmp(qs i,qs j){return i.ps<j.ps;}
#define calc(l1,r1,l2,r2) (min(r1,r2)-max(l1,l2)+1)
void add(int &x,int y,int l,int r,int ls,int rs,int dt){
    if(rs<ls) return;
    x=++cnt,sum[x]=sum[y],tot[x]=tot[y],L[x]=L[y],R[x]=R[y];
    if(ls<=l&&r<=rs){tot[x]+=dt;return;} int mid=((l+r)>>1);
    sum[x]+=(LL)calc(l,r,ls,rs)*(LL)dt;
    if(ls<=mid) add(L[x],L[y],l,mid,ls,rs,dt);
    if(rs>mid) add(R[x],R[y],mid+1,r,ls,rs,dt);
}
LL qry(int x,int y,int l,int r,int ls,int rs){
    LL res=(LL)calc(l,r,ls,rs)*(tot[x]-tot[y]);
    if(ls<=l&&r<=rs){return res+sum[x]-sum[y];} int mid=((l+r)>>1);
    if(ls<=mid) res+=qry(L[x],L[y],l,mid,ls,rs);
    if(rs>mid) res+=qry(R[x],R[y],mid+1,r,ls,rs); return res;
}
int main(){
    n=read(),T=read(),m1=read(),m2=read();
    for(int i=1;i<=n;i++) p[i]=read(),od[i]=i;
    for(int i=1;i<=n;i++){
        while(top&&p[S[top]]<p[i]) top--; last[i]=S[top],S[++top]=i;
    } top=0;
    for(int i=n;i;--i){
        while(top&&p[S[top]]<p[i]) top--; nxt[i]=S[top],S[++top]=i;
        if(!nxt[i]) nxt[i]=n+1;
    }
    for(int i=1;i<=n;i++){
        if(last[i]) q[++m]=qs(last[i],i+1,nxt[i]-1,m2);
        if(nxt[i]<=n) q[++m]=qs(nxt[i],last[i]+1,i-1,m2);
        if(last[i]>0&&nxt[i]<=n) q[++m]=qs(nxt[i],last[i],last[i],m1);
    } sort(q+1,q+m+1,cmp);
    for(int now=1,i=1;i<=n;i++){
        for(rt[i]=rt[i-1];now<=m&&q[now].ps<=i;now++)
            add(rt[q[now].ps],rt[q[now].ps],1,n,q[now].LS,q[now].RS,q[now].num);
    }
    while(T--){
        int t1=read(),t2=read(); LL ans;
        ans=qry(rt[t2],rt[t1-1],1,n,t1,t2);
        ans+=(LL)(t2-t1)*(LL)m1,write(ans),Putchar('\n');
    } flush(); return 0;
}

 

posted @ 2018-10-25 15:26  OYJason  阅读(186)  评论(0编辑  收藏  举报