zoukankan      html  css  js  c++  java
  • 线段树测试-2019.3.10

    线段树专题测试-2019.03.10

      又名:对拍的100种写挂方法。

      窗口的星星:https://www.luogu.org/problemnew/show/P1502

      其实挺简单的...发现窗框不算,所以想到将窗口大小横纵都减2,但是这样是错误的,因为窗口放置的位置可以是实数。只要不是两边都有星星卡在边界上,稍微挪动一下窗口就可以都看到了,减1即可。

      首先离散化一下,枚举下边界,维护以每个点作为左边界时可以看到的星星数,求个最大值就可以了。因为一颗星星可以被看见的左端点是一段连续的区间,所以用线段树来做。

      
     1 // luogu-judger-enable-o2
     2 # include <cstdio>
     3 # include <iostream>
     4 # include <algorithm>
     5 # include <cstring>
     6 # define R register int
     7 # define LL long long
     8 # define nl (n<<1)
     9 # define nr (n<<1|1)
    10 
    11 using namespace std;
    12 
    13 const int maxn=10005;
    14 int n,w,h,cnt,v[maxn],lef[maxn],T;
    15 LL ans=0,t[maxn<<2],d[maxn<<2];
    16 struct stars { int x,y,c; }a[maxn];
    17 struct num { int v,id; }c[maxn];
    18 
    19 bool cmpa (stars a,stars b) 
    20 {
    21     if(a.y==b.y) return a.x<b.x;
    22     return a.y<b.y;
    23 }
    24 
    25 bool cmpc (num a,num b) { return a.v<b.v; }
    26 
    27 void pushdown (int n)
    28 {
    29     LL x=d[n];
    30     d[n]=0;
    31     d[nl]+=x; t[nl]+=x;
    32     d[nr]+=x; t[nr]+=x;
    33 }
    34 
    35 void add (int n,int l,int r,int ll,int rr,int c)
    36 {
    37     if(ll<=l&&r<=rr)
    38     {
    39         t[n]+=c;
    40         d[n]+=c;
    41         return ;
    42     }
    43     int mid=(l+r)>>1;
    44     if(d[n]!=0) pushdown(n);
    45     if(ll<=mid) add(nl,l,mid,ll,rr,c);
    46     if(rr>mid) add(nr,mid+1,r,ll,rr,c);
    47     t[n]=max(t[nl],t[nr]);
    48 }
    49 
    50 int main()
    51 {
    52     scanf("%d",&T);
    53     while(T--)
    54     {
    55         scanf("%d%d%d",&n,&w,&h);
    56         w--,h--;
    57         for (R i=1;i<=n;++i)
    58         {
    59             scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);
    60             c[i].id=i,c[i].v=a[i].x;
    61         }
    62         sort(c+1,c+1+n,cmpc);
    63         cnt=0;
    64         for (R i=1;i<=n;++i)
    65         {
    66             if(i==1||c[i].v!=c[i-1].v) cnt++;
    67             a[ c[i].id ].x=cnt;
    68             v[cnt]=c[i].v;
    69         }
    70         int lw=1;
    71         for (R i=1;i<=cnt;++i)
    72         {
    73             while(v[i]-v[lw]>w) lw++;
    74             lef[i]=lw;
    75         }
    76         sort(a+1,a+1+n,cmpa);
    77         ans=0;
    78         int l=1,r=0,no;
    79         memset(t,0,sizeof(t));
    80         memset(d,0,sizeof(d));
    81         while(r<n)
    82         {
    83             r++;
    84             no=a[r].y;
    85             while(r<=n&&a[r].y==no) add(1,1,cnt,lef[ a[r].x ],a[r].x,a[r].c),r++; r--;
    86             while(a[r].y-a[l].y>h) add(1,1,cnt,lef[ a[l].x ],a[l].x,-a[l].c),l++;
    87             ans=max(ans,t[1]);
    88         }
    89         printf("%lld
    ",ans);
    90     }
    91     return 0;
    92 }
    窗口的星星

      矩形周长:https://www.luogu.org/problemnew/show/P1856

      应该说比刚刚那个题还要简单了,甚至不用离散化。先水平扫一遍再竖直扫一遍,看到每条矩形的第一条边就在这个区间+1,否则-1。每次统计被覆盖至少一次的线段长度,与上一次的值做差,求绝对值,加起来就是答案。一开始想得有点复杂,还想维护每个区间仅被覆盖一次的点的数量,后来才发现不用。首先不用下传标记,因为删除的必然是以前加入过的,所以每次修改的区间中的点一起被覆盖的次数都是一样的。更新答案时,如果这个区间被整体覆盖的次数大于0,那么显然就是它的长度,否则就是两儿子的长度和。

      
     1 // luogu-judger-enable-o2
     2 # include <cstdio>
     3 # include <iostream>
     4 # include <algorithm>
     5 # include <cstring>
     6 # define R register int
     7 # define LL long long
     8 # define nl (n<<1)
     9 # define nr (n<<1|1)
    10 
    11 using namespace std;
    12 
    13 const int maxn=20050;
    14 const int z=10005;
    15 int n,t[maxn<<3],f[maxn<<3],cnt=20020,a[maxn],b[maxn],c[maxn],d[maxn];
    16 LL ans=0;
    17 struct lin { int x,a,b,v; }l[maxn];
    18 
    19 bool cmp (lin a,lin b)
    20 {
    21     if(a.x==b.x) return a.v>b.v;
    22     return a.x>b.x;
    23 }
    24 
    25 int ab (int x) { if(x<0) return -x; return x; }
    26 
    27 void add (int n,int l,int r,int ll,int rr,int c)
    28 {
    29     if(ll<=l&&r<=rr)
    30     {
    31         f[n]+=c;
    32         if(f[n]==0) t[n]=t[nl]+t[nr];
    33         else t[n]=r-l+1;
    34         return ;
    35     }
    36     int mid=(l+r)>>1;
    37     if(ll<=mid) add(nl,l,mid,ll,rr,c);
    38     if(rr>mid) add(nr,mid+1,r,ll,rr,c);
    39     if(f[n]==0)
    40         t[n]=t[nl]+t[nr];
    41     else
    42         t[n]=r-l+1;
    43 }
    44 
    45 int main()
    46 {
    47     scanf("%d",&n);
    48     for (R i=1;i<=n;++i)
    49     {
    50         scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
    51         a[i]+=z,b[i]+=z,c[i]+=z,d[i]+=z;
    52     }
    53     for (R i=1;i<=n;++i)
    54     {
    55         l[2*i-1].x=b[i];
    56         l[2*i-1].a=a[i];
    57         l[2*i-1].b=c[i];
    58         l[2*i-1].v=-1;
    59         l[2*i].x=d[i];
    60         l[2*i].a=a[i];
    61         l[2*i].b=c[i];
    62         l[2*i].v=1;
    63     }
    64     sort(l+1,l+1+2*n,cmp);
    65     int las=0;
    66     for (R i=1;i<=2*n;++i)
    67     {
    68         add(1,1,cnt,l[i].a,l[i].b-1,l[i].v);
    69         ans+=ab(las-t[1]); las=t[1];
    70     }
    71     memset(t,0,sizeof(t));
    72     memset(f,0,sizeof(f));
    73     las=0;
    74     for (R i=1;i<=n;++i)
    75     {
    76         l[2*i-1].x=a[i];
    77         l[2*i-1].a=b[i];
    78         l[2*i-1].b=d[i];
    79         l[2*i-1].v=-1;
    80         l[2*i].x=c[i];
    81         l[2*i].a=b[i];
    82         l[2*i].b=d[i];
    83         l[2*i].v=1;
    84     }
    85     sort(l+1,l+1+2*n,cmp);
    86     for (R i=1;i<=2*n;++i)
    87     {
    88         add(1,1,cnt,l[i].a,l[i].b-1,l[i].v);
    89         ans+=ab(las-t[1]); las=t[1];
    90     }
    91     printf("%lld",ans);
    92     return 0;
    93 }
    矩形周长

      TET-Tetris 3D:https://www.luogu.org/problemnew/show/P3437

      首先考虑一下一维上的问题,需要一棵带标记的线段树维护。二维上稍微有点麻烦,如果只是简单的推广,会发现答案偏小。比如修改时修改了[1,2]这个区间的[2,3]区域,在查询[1,4]这个区间的[2,3]区域时是查不到答案的(显然不能每次更新都线段树合并)。这里有一个比较巧妙的思路,修改时,将所有与修改区间相交的区间的最大值进行更改,但不更改标记。被修改区间包含的那些区间仅更改标记。查询时,查询相交区间的标记和包含区间的值。正确性显然,可能只是一时间想不到,还是二维线段树做的太少了(根本就没做过好吧...)

      
     1 // luogu-judger-enable-o2
     2 # include <cstdio>
     3 # include <iostream>
     4 # include <algorithm>
     5 # include <cstring>
     6 # define R register int
     7 # define LL long long
     8 # define nl (n<<1)
     9 # define nr (n<<1|1)
    10 
    11 using namespace std;
    12 
    13 const int maxn=1005;
    14 int D,S,n,a,b,c,x,y;
    15 struct tre
    16 {
    17     int t[maxn*3+5],d[maxn*3+5];
    18     int ask (int n,int l,int r,int ll,int rr)
    19     {
    20         if(ll<=l&&r<=rr)
    21             return t[n];
    22         int mid=(l+r)>>1,ans=0;
    23         if(ll<=mid) ans=max(ans,ask(nl,l,mid,ll,rr));
    24         if(rr>mid) ans=max(ans,ask(nr,mid+1,r,ll,rr));
    25         return max(ans,d[n]);
    26     }
    27     void cov (int n,int l,int r,int ll,int rr,int c)
    28     {
    29         t[n]=max(t[n],c);
    30         if(ll<=l&&r<=rr)
    31         {
    32             d[n]=max(d[n],c);
    33             return;
    34         }
    35         int mid=(l+r)>>1;
    36         if(ll<=mid) cov(nl,l,mid,ll,rr,c);
    37         if(rr>mid) cov(nr,mid+1,r,ll,rr,c);
    38     }
    39 }t[maxn*3+5],d[maxn*3+5];
    40 
    41 int ask (int n,int l,int r,int ll,int rr,int a,int b)
    42 {
    43     if(ll<=l&&r<=rr) return t[n].ask(1,1,S,a,b);
    44     int mid=(l+r)>>1,ans=d[n].ask(1,1,S,a,b);
    45     if(ll<=mid) ans=max(ans,ask(nl,l,mid,ll,rr,a,b));
    46     if(rr>mid) ans=max(ans,ask(nr,mid+1,r,ll,rr,a,b));
    47     return ans;
    48 }
    49 
    50 void cov (int n,int l,int r,int ll,int rr,int a,int b,int h)
    51 {
    52     t[n].cov(1,1,S,a,b,h);
    53     if(ll<=l&&r<=rr) d[n].cov(1,1,S,a,b,h);
    54     else
    55     {
    56         int mid=(l+r)>>1;
    57         if(ll<=mid) cov(nl,l,mid,ll,rr,a,b,h);
    58         if(rr>mid) cov(nr,mid+1,r,ll,rr,a,b,h);
    59     }
    60 }
    61 
    62 int main()
    63 {
    64     scanf("%d%d%d",&D,&S,&n);
    65     int h=0;
    66     for (R i=1;i<=n;++i)
    67     {
    68         scanf("%d%d%d%d%d",&x,&y,&c,&a,&b);
    69         x=a+x,y=b+y;
    70         a++,b++;
    71         h=0;
    72         h=ask(1,1,D,a,x,b,y);
    73         h+=c;
    74         cov(1,1,D,a,x,b,y,h);
    75     }
    76     int ans=0;
    77     ans=ask(1,1,D,1,D,1,S);
    78     printf("%d",ans);
    79     return 0;
    80 }
    TET

     

      考试的时候,先写了前两题。这时候好像还很早,所以没有对拍,而是先去开第三题了。结果刚开始看第三题就开始头晕,所以做了很久很久也没想到怎么做,最后写了一个这么一个做法:对于每一行开线段树,每次暴力修改每一棵线段树的相应位置。这时已经没有多少时间了,所以就去写对拍。因为时间不够,对拍写的也很粗糙,第二题的暴力只能处理坐标为正数的情况,所以就只拍了这部分,过了就没管了。然而...负数部分是错的,把负数移至正数的最大值设的太大,所以数组就不够用了,数据又很强,就爆零了。然后写第一题的暴力,怎么拍怎么错,于是抱着凉凉的心态把题交上去了。但是它过了!竟然是暴力写错了...第三题的线段树优化暴力喜提70分,暴力真是个好算法。总结一下这次考试:对拍过了的爆零了,对拍过不了的AC了,看来以后还是静态查错比较靠谱。另:cena为什么总是把我的程序吃掉换成别人的啊...  

  • 相关阅读:
    表单提交:button input submit 的区别
    JavaScript中改变this指针的注意事项
    宝塔服务器配置nginx刷新404的问题汇总
    ES6笔记整理
    axios网络请求
    v-model双向绑定
    v-bind动态绑定
    前端模块化
    vue router 路由
    JS高阶函数
  • 原文地址:https://www.cnblogs.com/shzr/p/10511675.html
Copyright © 2011-2022 走看看