zoukankan      html  css  js  c++  java
  • 刷题总结——影魔(HNOI2017 BZOJ4826 线段树+扫描线)

    题目:

    Description

    影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。千百年来,他收集了各式各样
    的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。每一个灵魂,都有着自己的战斗力,而影魔,靠
    这些战斗力提升自己的攻击。奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。
    第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i,j(i<j)来说,若不存在 k[s](i
    <s<j)大于 k[i]或者 k[j],则会为影魔提供 p1 的攻击力(可理解为:当 j=i+1 时,因为不存在满足 i<s<j 的 s,从
    而 k[s]不存在,这时提供 p1 的攻击力;当 j>i+1 时,若max{k[s]|i<s<j}<=min{k[i],k[j]} , 则 提 供 p1 的 攻
     击 力 ); 另 一 种 情 况 , 令 c 为k[i+1],k[i+2],k[i+3]......k[j-1]的最大值,若 c 满足:k[i]<c<k[j],或
    者 k[j]<c<k[i],则会为影魔提供 p2 的攻击力,当这样的 c 不存在时,自然不会提供这 p2 的攻击力;其他情况的
    点对,均不会为影魔提供攻击力。影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任
    意一段区间[a,b],1<=a<b<=n,位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑 所有满足a<=i<j<=b 的灵
    魂对 i,j 提供的攻击力之和。顺带一提,灵魂的战斗力组成一个 1 到 n 的排列:k[1],k[2],...,k[n]。

    Input

    第一行 n,m,p1,p2
    第二行 n 个数:k[1],k[2],...,k[n]
    接下来 m 行,每行两个数 a,b,表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。
    1 <= n,m <= 200000;1 <= p1,p2 <= 1000

    Output

    共输出 m 行,每行一个答案,依次对应 m 个询问。

    Sample Input

    10 5 2 3
    7 9 5 1 3 10 6 8 2 4
    1 7
    1 9
    1 3
    5 9
    1 5

    Sample Output

    30
    39
    4
    13
    16

    HINT

    Source

    题解:

    引用下lcf2000的题解,%%%%%%%%

    我们枚举最大值的位置i,找出左边第一个比ai大的位置l,右边第一个比ai大的位置r,然后我们分开考虑一下p1和p2的贡献。

    首先由于ai为最大值,那么左端点不会小于l,右端点不会大于r。

    容易发现只有左端点为l,右端点为r才会产生p1的贡献然后产生p2贡献的有两种:一种是左端点为l,右端点在区间(i,r)中;另一种是左端点区间(l,i)中,右端点为r。

    还有一种情况需要考虑,就是左端点和右端点差为1,会产生p1的贡献。对每个询问直接计算就可以了。

    所以这个问题可以抽象到二维平面上。有一些点和一些线段都有权值,每次询问某个矩形内部的权值和。

    于是离线排序+扫描线+树状数组即可。

    自己再说一两句吧···

    这道题最先开始要想到的是每次考虑对答案贡献的时候首先要想到的是枚举中间的点···而不是两旁·····

    其实这种考虑贡献对象的思路在很多地方都有用到···比如有时做与树有关的题很多时候我们考虑对答案贡献都是考虑每一条边·····希望下次遇到这种题能记住这种思想···

    然后就是转二维平面····这个其实比较好想···毕竟看到了点对嘛···

    想到转二维那么扫描线和线段树(我是用线段树写的··对带区间修改的树状数组不是太熟···)就是顺理成章的的事情了···

    然而转二维时有个细节要注意····一个点可能没有左边第一个比自己大的位置l,或者没有右边第一个比自己大的位置r,如果仅仅是这二者中间少了一个···它也是会对答案产生贡献的···比如7 9 10中的9·····md因为这一点被样例卡着过不去23333

    然后就是排序了···修改一定要排在询问前面当高度相同时··这点在cdq分治时其实经常提到·····一定要注意了

    最后吐槽一句洛谷为毛要卡线段树啊艹···现在哪有放树状数组不放线段树的···还是bzoj不坑···

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    const int N=2e5+5;
    struct node
    {
      int x,y,h,v,id;
    }q[N*8];
    int n,m,p1,p2,stack[N],top,num[N],Le[N],Ri[N],cnt,tag[N*4];
    long long ans[N],tree[N*4];
    inline int R()
    {
      char c;int f=0;
      for(c=getchar();c<'0'||c>'9';c=getchar());
      for(;c<='9'&&c>='0';c=getchar())  
        f=(f<<3)+(f<<1)+c-'0';
      return f;
    }
    inline bool cmp(node a,node b)
    {
      if(a.h==b.h)  return a.id<b.id;
      return a.h<b.h;
    }
    inline void add(int k,int l,int r,int v)
    {
      tree[k]+=(long long)(r-l+1)*v;
      tag[k]+=v;
    }
    inline void pushdown(int k,int l,int r,int mid)
    {
      if(tag[k])
      {
        add(k*2,l,mid,tag[k]);
        add(k*2+1,mid+1,r,tag[k]);
        tag[k]=0;
      }
    }
    inline void modify(int k,int l,int r,int x,int y,int v)
    {
      if(l>=x&&r<=y)
      {
        add(k,l,r,v);
        return;
      }
      int mid=(l+r)/2;
      pushdown(k,l,r,mid);
      if(x<=mid)  modify(k*2,l,mid,x,y,v);
      if(y>mid)  modify(k*2+1,mid+1,r,x,y,v);
      tree[k]=tree[k*2]+tree[k*2+1];
    }
    inline long long query(int k,int l,int r,int x,int y)
    {
      if(l>=x&&r<=y)
        return tree[k];
      int mid=(l+r)/2;
      pushdown(k,l,r,mid);long long temp=0;
      if(x<=mid)  temp+=query(k*2,l,mid,x,y);
      if(y>mid)  temp+=query(k*2+1,mid+1,r,x,y);
      return temp;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      n=R(),m=R(),p1=R(),p2=R();int a,b;
      for(int i=1;i<=n;i++)  num[i]=R();
      for(int i=1;i<=n;i++) 
      {
        while(top&&num[i]>num[stack[top]])  Ri[stack[top]]=i,top--;
        if(top)  Le[i]=stack[top];
        stack[++top]=i;
      }
      for(int i=1;i<=m;i++)
      {
        a=R(),b=R();ans[i]=(long long)(b-a)*p1;
        q[++cnt].x=a,q[cnt].y=b,q[cnt].h=a-1,q[cnt].v=-1;q[cnt].id=i;
        q[++cnt].x=a,q[cnt].y=b,q[cnt].h=b,q[cnt].v=1;q[cnt].id=i;
      }
      for(int i=1;i<=n;i++)
      {
        if(Ri[i]&&Le[i])
        {
          q[++cnt].x=Le[i],q[cnt].y=Le[i],q[cnt].h=Ri[i],q[cnt].v=p1;
          if(Le[i]<i-1)  q[++cnt].x=Le[i]+1,q[cnt].y=i-1,q[cnt].h=Ri[i],q[cnt].v=p2;
          if(Ri[i]>i+1)  q[++cnt].x=i+1,q[cnt].y=Ri[i]-1,q[cnt].h=Le[i],q[cnt].v=p2;
        }
        else if(Ri[i])
        {
          if(i>1)  q[++cnt].x=1,q[cnt].y=i-1,q[cnt].h=Ri[i],q[cnt].v=p2;
        }
        else if(Le[i])
        {
          if(i<n)  q[++cnt].x=i+1,q[cnt].y=n,q[cnt].h=Le[i],q[cnt].v=p2;
        }
      }
      sort(q+1,q+cnt+1,cmp);
      for(int i=1;i<=cnt;i++)
      {
        if(q[i].id)
          ans[q[i].id]+=(long long)q[i].v*query(1,1,n,q[i].x,q[i].y);  
        else
          modify(1,1,n,q[i].x,q[i].y,q[i].v);
      }
      for(int i=1;i<=m;i++)
        printf("%lld
    ",ans[i]);
      return 0;
    }
     
  • 相关阅读:
    BizTalk2010动手实验(二)第一个BizTalk应用
    基于NopCommerce的开源电商系统改造总结
    BizTalk动手实验(六)Orchestration开发
    BizTalk动手实验(三)BizTalk开发综合实验
    项目管理-自上而下还是自下而上的沟通?
    BizTalk 2013 Beta 新特性介绍
    自主开发与带兵打仗
    Word邮件合并IT男必备技能
    三年项目管理,三个阶段
    BizTalk动手实验(八)消息路由
  • 原文地址:https://www.cnblogs.com/AseanA/p/7597194.html
Copyright © 2011-2022 走看看