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

    题目

    [AH2017/HNOI2017]影魔

    做法

    做法1(口胡)

    这个做法估计会T

    考虑每个数字连向后面第一个比他大的树,对一个区间建一颗树,对于(i<j<k,a_i<a_j<a_k)的询问,即为树上每个点(dep-2)之和,然后其余类似,用莫队维护,时间复杂度:(O(nsqrt{n})),常数和码量都巨大,于是没有打,膜了题解。

    当然,至于为什么为(dep-2)之和,因为我们默认(i<j<k,a_i<a_j<a_k)是由((i,k))区间中最大的数字来找到这个区间。

    做法2

    以下摘自此博客

    这个题可以采取离线处理的方式.先处理出每个点i左边第一个比它大的点L[i],和右边第一个比它大的点R[i].

    那么对于区间L[i]到R[i]有p1的贡献.①

    对于左端点在L[i]+1到i-1,右端点为R[i]的区间有p2的贡献.②

    对于左端点为L[i],右端点为i+1到R[i]-1的区间也有p2的贡献.③

    所以我们离线排序处理好.

    对于①情况,我们在扫到R[i]时,更新点L[i]的贡献

    对于②情况,我们在扫到R[i]时,更新区间L[i]+1到i-1的贡献

    对于③情况,我们在扫到L[i]时,更新区间i+1到R[i]-1的贡献

    我们对于每个询问[l,r],在扫到l-1时,我们记录此时区间l到r的每个点的贡献和为sum1,然后当我们扫到r的时候,再次记录此时的区间l到r的每个点的贡献和为sum2,显然答案就是sum2-sum1了.

    好,至于为什么吗,我来解释一下,为什么(p1)的贡献只用(L[i],R[i])来统计呢?(当然,这也可以证明(p1)区间个数实在(O(n))级别的)

    1. 为什么每个区间必定会被统计且只会被统计一次:首先,考虑这个((l,r))是个合法的(p1)区间,那么对于((l,r))范围中最大的数字(mid),其(L[i]=l,R[i]=r),且对于这个区间其他数字,绝对不可能(L[i]=l,R[i]=r),因为(mid)比他们都打,卡在中间挡住了他们。
    2. 为什么((L[i],R[i]))一定构成一个合法区间:这不废话?

    对于(p2)(a_i<a_j<a_k),证明方法类似,每个区间由这个区间中最大的数字来统计,(a_{i}>a_{j}>a_{k})

    当然,别忘了((i,i+1))固定有(p1)的贡献。

    时间复杂度:(O(nlogn))

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define  N  210000
    #define  NN  410000
    #define  NNN  610000
    using  namespace  std;
    typedef  long  long  LL;
    struct  node
    {
    	int  l,r,d;
    	LL  lazy,c;
    }tr[NN];int  len,last[N];
    inline  void  pushlazy(int  x,LL  k){tr[x].lazy+=k;tr[x].c+=k*tr[x].d;}
    inline  void  updata(int  x){tr[x].c=tr[tr[x].l].c+tr[tr[x].r].c;}
    inline  void  downdata(int  x)
    {
    	if(tr[x].lazy)
    	{
    		pushlazy(tr[x].l,tr[x].lazy);
    		pushlazy(tr[x].r,tr[x].lazy);
    		tr[x].lazy=0;
    	}
    }
    void  bt(int  l,int  r)
    {
    	int  now=++len;tr[now].d=(r-l+1);
    	if(l<r)
    	{
    		int  mid=(l+r)>>1;
    		tr[now].l=len+1;bt(l,mid);
    		tr[now].r=len+1;bt(mid+1,r);
    	}
    }
    void  change(int  now,int  l,int  r,int  ll,int  rr,LL  k)
    {
    	if(l==ll  &&  r==rr){pushlazy(now,k);return  ;}
    	int  mid=(l+r)>>1;
    	downdata(now);
    	if(rr<=mid)change(tr[now].l,l,mid,ll,rr,k);
    	else  if(mid<ll)change(tr[now].r,mid+1,r,ll,rr,k);
    	else  change(tr[now].l,l,mid,ll,mid,k),change(tr[now].r,mid+1,r,mid+1,rr,k);
    	updata(now);
    }
    LL  findans(int  now,int  l,int  r,int  ll,int  rr)
    {
    	if(l==ll  &&  r==rr)return  tr[now].c;
    	int  mid=(l+r)>>1;
    	downdata(now);
    	if(rr<=mid)return  findans(tr[now].l,l,mid,ll,rr);
    	else  if(mid<ll)return  findans(tr[now].r,mid+1,r,ll,rr);
    	else  return  findans(tr[now].l,l,mid,ll,mid)+findans(tr[now].r,mid+1,r,mid+1,rr);	
    }
    int  L[N],R[N],n,m,a[N];
    LL  q1,q2;
    int  sta[N],top;
    struct  Query
    {
    	int  l,r;
    	LL  *id,type;
    };
    struct  CHANGE
    {
    	int  l,r,k,id/*在访问到哪个人时会使用这个*/;
    }ch[NNN];int  clen,cnow=1;
    inline  bool  cmp(CHANGE  x,CHANGE  y){return  x.id<y.id;}
    vector<Query> fuck[N];
    LL  ans[N];
    inline  void  solve(int  x/*加入第x个位置*/)
    {
    	while(cnow<=clen  &&  ch[cnow].id==x)
    	{
    		if(ch[cnow].l<=ch[cnow].r)change(1,1,n,ch[cnow].l,ch[cnow].r,ch[cnow].k);
    		cnow++;
    	}
    	for(int  i=0;i<fuck[x].size();i++)
    	{
    		Query  y=fuck[x][i];
    		(*y.id)+=y.type*findans(1,1,n,y.l,y.r);
    	}
    }
    int  main()
    {
    	scanf("%d%d%d%d",&n,&m,&q1,&q2);
    	for(int  i=1;i<=n;i++)scanf("%d",&a[i]);
    	for(int  i=1;i<=n;i++)
    	{
    		while(top  &&  a[sta[top]]<a[i])top--;
    		L[i]=sta[top];
    		sta[++top]=i;
    	}
    	top=0;
    	for(int  i=n;i>=1;i--)
    	{
    		while(top  &&  a[sta[top]]<a[i])top--;
    		R[i]=sta[top];
    		sta[++top]=i;
    	}
    	for(int  i=1;i<=n;i++)
    	{
    		if(L[i]  &&  R[i])
    		{
    			ch[++clen].id=R[i];ch[clen].k=q1;ch[clen].l=L[i];ch[clen].r=L[i];
    		}
    		if(R[i])
    		{
    			ch[++clen].id=R[i];ch[clen].k=q2;ch[clen].l=L[i]+1;ch[clen].r=i-1;
    		}
    		if(L[i])
    		{
    			ch[++clen].id=L[i];ch[clen].k=q2;ch[clen].l=i+1;ch[clen].r=!R[i]?n:R[i]-1;
    		}
    	}
    	bt(1,n);
    	sort(ch+1,ch+clen+1,cmp);
    	for(int  i=1;i<=m;i++)
    	{
    		int  l,r;scanf("%d%d",&l,&r);
    		ans[i]=(r-l)*q1;
    		Query  x;x.l=l;x.r=r;x.type=-1;x.id=&ans[i];
    		fuck[l-1].push_back(x);
    		x.type=1;x.id=&ans[i];
    		fuck[r].push_back(x);
    	}
    	for(int  i=1;i<=n;i++)
    	{
    		solve(i);
    	}
    	for(int  i=1;i<=m;i++)printf("%lld
    ",ans[i]);
    	return  0;
    }
    
  • 相关阅读:
    vi/vim系统编辑命令使用技巧
    C++基础之智能指针
    C++基础之volatile关键字
    C++基础之强制类型转换
    C++基础之左值、右值与移动语义
    C++基础之对象模型
    C++基础之运行时类型识别RTTI
    C++基础之指针与引用的底层实现
    深度学习之参数计算(CNN为例)
    数学基础之勾股数
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13933663.html
Copyright © 2011-2022 走看看