zoukankan      html  css  js  c++  java
  • ●BZOJ 4826 [Hnoi2017]影魔

    题链:

    http://www.lydsy.com/JudgeOnline/problem.php?id=4826

    题解:

    主席树,单调栈

    以前还没做过这种维护信息的题,感觉好奇妙。

    每对相邻的两个数所贡献的 P1 就直接在最后加入答案就好了,
    以下是处理存在 s (i<s<j)的情况。
    首先用单调栈维护出 L[i], R[i]分别表示 i 点左边和右边第一个值大于 K[i]的位置。
    然后不难发现,如果对应的 L[i] 或 R[i] 存在于 K 数组中,那么
    点对 (L[i],R[i]) 可以贡献一个 P1
    点对 (L[i],i+1~R[i]-1) 每个都可以贡献一个 P2
    点对 (L[i]+1~i-1,R[i]) 每个都可以贡献一个 P2
    然后把这些点对看成是平面上的点,
    那么就可以用两个主席树来处理好信息后,然后在线查询。
    怎么维护呢?
    注意到我们需要的是对平面的某个点或者是某条线段上的点进行值的累加。
    且线段又只存在平行于 x 轴的和平行于 y 轴的。
    所以两个主席树分别对应着维护平行着某一轴的线段的信息就好了。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define ll long long
    #define filein(x) freopen(#x".in","r",stdin)
    #define fileout(x) freopen(#x".out","w",stdout)
    using namespace std;
    const int MAXN=2e5+5;
    int A[MAXN];
    int N,M,P1,P2;
    struct List{
    	int L[MAXN*2],R[MAXN*2],W[MAXN*2],Nxt[MAXN*2],Head[MAXN],lnt;
    	void Reset(){
    		lnt=2; memset(Head,0,sizeof(Head));
    	}
    	void Add(int p,int l,int r,int w){
    		L[lnt]=l; R[lnt]=r; W[lnt]=w; 
    		Nxt[lnt]=Head[p]; Head[p]=lnt++;
    	}
    }l1,l2;
    struct CMT{
    	#define len ((r<ar?r:ar)-(l>al?l:al)+1)
    	int rt[MAXN],ls[MAXN*50],rs[MAXN*50],sz; ll Sum[MAXN*50],Add[MAXN*50];
    	void Insert(int &u,int l,int r,int al,int ar,int w){
    		++sz; ls[sz]=ls[u]; rs[sz]=rs[u];
    		Sum[sz]=Sum[u]; Add[sz]=Add[u]; u=sz;
    		if(al<=l&&r<=ar){
    			Add[u]+=w; Sum[u]+=len*w;
    			return;
    		}
    		int mid=(l+r)>>1; Sum[u]+=len*w;
    		if(al<=mid) Insert(ls[u],l,mid,al,ar,w);
    		if(mid<ar) Insert(rs[u],mid+1,r,al,ar,w);
    	}
    	ll Query(int v,int u,int l,int r,int al,int ar){
    		if(!u) return 0;
    		if(al<=l&&r<=ar) return Sum[u]-Sum[v];
    		int mid=(l+r)>>1; ll ret=(Add[u]-Add[v])*(len);
    		if(al<=mid) ret+=Query(ls[v],ls[u],l,mid,al,ar);
    		if(mid<ar) ret+=Query(rs[v],rs[u],mid+1,r,al,ar);
    		return ret;
    	}
    	void Build(const List &li){
    		for(int p=1;p<=N;p++){
    			rt[p]=rt[p-1];
    			for(int i=li.Head[p],l,r,w;i;i=li.Nxt[i]){
    				l=li.L[i]; r=li.R[i]; w=li.W[i];
    				Insert(rt[p],1,N,l,r,w);
    			}
    		}
    	}
    	ll Query(int l,int r){
    		return Query(rt[l-1],rt[r],1,N,l,r);
    	}
    	#undef len
    }T1,T2;
    char gc(){
    	return getchar();
    	static char s[MAXN];
    	static int bit=200000,p,len;
    	if(p>=len) len=fread(s,1,bit,stdin),s[len]=EOF,p=0; 
    	return s[p++];
    }
    void read(int &x){
    	static int f; static char ch;
    	x=0; f=1; ch=gc();
    	while(ch<'0'||'9'<ch){if(ch=='-') f=-1;ch=gc();}
    	while('0'<=ch&&ch<='9'){x=x*10+ch-'0'; ch=gc();}
    	x=x*f;
    }
    void pre(){
    	static int L[MAXN],R[MAXN],stk[MAXN],stp[MAXN],top=0;
    	for(int i=1;i<=N;i++){
    		while(top&&A[i]>stk[top]) R[stp[top]]=i,top--;
    		L[i]=stp[top]; top++;
    		stk[top]=A[i]; stp[top]=i;	
    	}
    	while(top) R[stp[top--]]=N+1;
    	for(int i=1;i<=N;i++){
    		if(1<=L[i]&&R[i]<=N) l1.Add(L[i],R[i],R[i],P1);
    		if(1<=L[i]&&i+1<=R[i]-1) l1.Add(L[i],i+1,R[i]-1,P2);
    		if(L[i]+1<=i-1&&R[i]<=N) l2.Add(R[i],L[i]+1,i-1,P2);
    	}
    	T1.Build(l1); T2.Build(l2);
    } 
    int main(){
    	//filein(4826);
    	l1.Reset(); l2.Reset();
    	read(N); read(M); read(P1); read(P2);
    	for(int i=1;i<=N;i++) read(A[i]);
    	pre();
    	for(int i=1,l,r;i<=M;i++){
    		read(l); read(r); 
    		printf("%lld
    ",T1.Query(l,r)+T2.Query(l,r)+1ll*(r-l)*P1);
    	}
    	return 0;
    }

  • 相关阅读:
    反黑战役之谁动了我的文件?
    poj 1611 The Suspects
    Effective C++ 条款44
    不说技术~那些有文化的人们所说的各大主义,其实百度上都有
    DDD~Unity在DDD中的使用
    DDD~领域层
    知方可补不足~SQL中的count命令的一些优化措施(百万以上数据明显)
    将不确定变为确定~感谢异或,是你让我彻底摆脱“否定式”
    php按照奖品百分比随机抽奖代码分析
    linux下Ftp环境的搭建
  • 原文地址:https://www.cnblogs.com/zj75211/p/8065313.html
Copyright © 2011-2022 走看看