zoukankan      html  css  js  c++  java
  • 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]$求仅考虑$lleq i< jleq 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 imes|[l,r]cap[k+1,j-1]|$的贡献

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

    如果$i,j$均出现在$[l,r]$中,那么这个选$i,j$还会有额外$p_1$的贡献,这个可以等价于如果$i$出现在$[l,r]$中那么一定会有$p_1 imes|[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]$,把它看做$posin[1,l-1]$中$[l,r]$的区间和与$posin[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('
    ');
    	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('
    ');
        } flush(); return 0;
    }
  • 相关阅读:
    regex正则表达式
    openfire+asmack
    vim 粘贴 取消缩进zz
    selenium自动化实战基于python语言(二: 编写脚本)
    Gparted硬盘管理工具
    selenium自动化实战基于python语言(一: 编写脚本)
    selenium自动化实战基于python语言(环境搭建)
    使Eclipse代码自动提示
    String相关的常见问题
    在Eclipse中查看JDK类库的源代码
  • 原文地址:https://www.cnblogs.com/OYJason/p/9850116.html
Copyright © 2011-2022 走看看