【HDOJ2019网络赛】 1002 Rikka with Cake

题目:http://acm.hdu.edu.cn/showproblem.php?pid=6681

大意:对一个 n*m 矩形切 k 刀,每一刀的刀尖从(x, y)开始直到划到边界,保证每一次的操作的x和y都不同,矩形左下角(0, 0),右上角(n, m),输出最终矩形被分为几个部分

L、R、U、D代表刀向左、右、上、下划开

如下图分别是

 

1 1 U

2 2 L

3 3 L

 


 
1 2 R
3 1 U
4 3 L
2 4 D

的操作结果

矩形分别被分为3个部分和5个部分

 

一个重要结论是矩形的部分数目 = 交点数目 + 1

证明:一个交点代表了一个直角的形成,直角的两条边向边界延展,如果中途没有和另一个直角的两条边恰好形成新矩形,那么这两条边就都会和边界相交,与边界形成新的矩形。由于每一次操作的 x 和 y 都不同,因此除去所有新矩形,一定还有一个剩余的部分。所以分块的数目  = 交点数目 + 1

因此用线段树处理出交点数,线段树按 x 坐标建立,维护经过每个点的横向操作数目。

由于矩形大( 边长 1e9 ),先对每个操作的点做离散化,按 x 坐标大小进行编号。

第一次建立线段树,按 y 坐标从大到小处理 k次操作,保证纵向线段在查询时得到的一定是能切割到的横向线段数目。如果操作是 ‘L ’或者 ‘R’ 方向则将该次操作覆盖的区间都 +1 ,如果操作是 ‘U’ 则对操作的点进行查询

 

例如上图。

第一次操作:5 8 L  此时 x=1 到 x=5 维护的横线数目是 1

第二次操作:3 6 R 此时 x=1 到 x=2 维护的横线数目是 1,x=3 到 x=5 维护的横线数目是 2,x=6 到 x=13 维护的横线数目是 1

第三次操作:4 4 U 此时对 x=4 查询,得到该操作的交点数 = 2

.......

第二次建立线段树,按 y 坐标从小到大处理 k 次操作,如果操作是 ‘L ’或者 ‘R’ 方向则将该次操作覆盖的区间都 +1 ,如果操作是 ‘D’ 则对操作的点进行查询

 

#include<bits/stdc++.h>
using namespace std;
int T;
int n,m,k;
struct instruction{
    int p;
    int x,y;
    char d;
}a[100005];
struct node{
    int l,r,c;
    int lazy;
}tree[100005*4];
int ans;
bool cmp(instruction in1, instruction in2)
{
    return in1.x<in2.x;
}
bool cmp1(instruction in1, instruction in2)
{
    return in1.y>in2.y;
}
bool cmp2(instruction in1,instruction in2)
{
    return in1.y<in2.y;
}
void build(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    tree[now].c=tree[now].lazy=0;
    if(l==r) return ;
    build(now*2,l,(l+r)/2);
    build(now*2+1,(l+r)/2+1,r);
    return ;
}
void update(int now)
{
    int w,len;
    len=tree[now*2].r-tree[now*2].l+1;
    w=tree[now].lazy;
    tree[now*2].lazy+=w;
    tree[now*2].c+=w*len;
    len=tree[now*2+1].r-tree[now*2+1].l+1;
    tree[now*2+1].lazy+=w;
    tree[now*2+1].c+=w*len;
    tree[now].lazy=0;
    return ;
}

//区间修改 
void addin(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r) 
    {
        tree[now].c+=(tree[now].r-tree[now].l+1);
        tree[now].lazy++;
        return ; 
    }
    int mid=(tree[now].l+tree[now].r)/2;
    
    //传递标记 
     if(tree[now].lazy) update(now);
    if(l<=mid) addin(now*2,l,r);
    if(r>mid) addin(now*2+1,l,r);
    return ;
 } 
 
//单点查询 
void qurry(int now,int x)
{
    if(tree[now].l==tree[now].r) 
    {
        ans+=tree[now].c;return ;
    }
    
    //传递标记 
    if(tree[now].lazy) update(now);
    int mid=(tree[now].l+tree[now].r)/2;
    if(x<=mid) qurry(now*2,x);
    if(x>mid) qurry(now*2+1,x);
    return ;
}
void work()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++)
    {
        char s[2];
        scanf("%d%d",&a[i].x,&a[i].y);
        scanf("%s",s);
        a[i].d=s[0];
    }
    
    //离散化,按x从小到大编号 ,矩形大小离散为 k*k 
    sort(a+1,a+k+1,cmp);
    for(int i=1;i<=k;i++) a[i].p=i;
    
    //处理切割方向向上的查询 
    sort(a+1,a+k+1,cmp1);
    build(1,1,k);
    for(int i=1;i<=k;i++)
    {    
        if(a[i].d=='L') addin(1,1,a[i].p);
        if(a[i].d=='R') addin(1,a[i].p,k);
        if(a[i].d=='U') qurry(1,a[i].p);
    }
     
    //处理切割方向向下的查询 
    sort(a+1,a+k+1,cmp2);
    //重新建树,清空树里的值 
    build(1,1,k);
    for(int i=1;i<=k;i++)
    {
        if(a[i].d=='L') addin(1,1,a[i].p);
        if(a[i].d=='R') addin(1,a[i].p,k);
        if(a[i].d=='D') qurry(1,a[i].p);
    }
    printf("%d\n",ans+1);
    
 } 
 int main()
 {
     scanf("%d",&T);
     for(int i=1;i<=T;i++) 
     {
         work();
         ans=0;
     }
     return 0;
 }
View Code

 

 

 

队友的树状数组只写了 75 行 (。•́︿•̀。)

 

posted @ 2019-08-20 20:43  Oranges  阅读(136)  评论(0编辑  收藏  举报