HDU - 4419 Colourful Rectangle【扫描线.笔记】

题目链接:http://acm.hdu.edu.cn/viewcode.php?rid=32159923
题目大意:有n个矩形块,这些矩形块有R, G, B三种不同的颜色,最终区域的颜色由该区域上颜色的种类决定,要求输出 R, G, B, RG, RB, GB, RGB七种颜色的面积是多少。

思路:在前面区域覆盖面积的例题里,我们用一维数组维护被覆盖的区间长度,因为这里有颜色之分,所以可以再开一维表示颜色。此外,区域的颜色是由不同颜色的种类决定的,所以颜色的判断就可以很好地利用到或运算。

  • (扫描线中为什么不用push_down)扫描线的例题中利用了线段树这种数据结构,但是我们每次查询的都是整体,没有去询问某个子区间,且最终结果与覆盖顺序无关,所以不用push_down。

AC1:
//cover[root][i]表示区间被第i种颜色覆盖的次数

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define mid ((l + r)>>1)
#define chl root<<1
#define chr root<<1|1
using namespace std;
typedef unsigned long long ull;
typedef long long LL;
const int manx=2e4+10;
int sum[manx<<3][8],cover[manx<<3][3],X[manx<<1],n;
struct node
{
    int y,lx,rx,color;
    int val;
    friend bool operator<(node a,node b)
    {
        return a.y<b.y;
    }
}line[manx<<1];
void init()
{
    memset(sum,0,sizeof(sum));
    memset(cover,0,sizeof(cover));
}
void eval(int root,int l,int r)
{
    int white=X[r+1]-X[l],Color=0;
    for(int i=0; i<=2; i++)
        if(cover[root][i])
            Color|=(1<<i);
    for(int i=1; i<=7; i++)
        sum[root][i]=0;
    for(int i=1; i<=7; i++)
    {
        sum[root][i|Color]+=sum[chl][i]+sum[chr][i];
        white-=sum[chl][i]+sum[chr][i];
    }
    if(Color)
        sum[root][Color]+=white;
}
void change(int root,int l,int r,int ll,int rr,int color,int val)
{
    if(l==ll&&r==rr)
    {
        cover[root][color]+=val;
        eval(root,l,r);
        return;
    }
    if(rr<=mid)
        change(chl,l,mid,ll,rr,color,val);
    else if(ll>mid)
        change(chr,mid+1,r,ll,rr,color,val);
    else
    {
        change(chl,l,mid,ll,mid,color,val);
        change(chr,mid+1,r,mid+1,rr,color,val);
    }
    eval(root,l,r);
}
int main()
{
    int t,x1,y1,x2,y2,Cas=0;;
    char str[2];
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init();
        for(int i=1; i<=n; i++)
        {
            int color;
            scanf("%s%d%d%d%d",str,&x1,&y1,&x2,&y2);
            if(str[0]=='R')
                color=2;
            else if(str[0]=='G')
                color=1;
            else
                color=0;
            line[2*i]=node{y1,x1,x2,color,1};
            X[2*i]=x1;
            line[2*i-1]=node{y2,x1,x2,color,-1};
            X[2*i-1]=x2;
        }
        sort(line+1,line+2*n+1);
        sort(X+1,X+2*n+1);
        int cnt=unique(X+1,X+2*n+1)-X-1;
        LL ans[8]= {0};
        for(int i=1; i<2*n; i++)
        {
            int l=lower_bound(X+1,X+cnt+1,line[i].lx)-X;
            int r=lower_bound(X+1,X+cnt+1,line[i].rx)-X-1;
            int dy=line[i+1].y-line[i].y;
            change(1,1,cnt,l,r,line[i].color,line[i].val);
            for(int i=1; i<=7; i++)
                ans[i]+=(LL)sum[1][i]*(LL)dy;
        }
        printf("Case %d:\n",++Cas);
        printf("%lld\n%lld\n%lld\n%lld\n%lld\n%lld\n%lld\n",ans[4],ans[2],ans[1],ans[6],ans[5],ans[3],ans[7]);
    }
    return 0;
}

AC2:
记录一下当时找bug时的心情。。真的MLE到哭泣。
在这里插入图片描述
本来觉得找到了扫描线的快乐,后面把sum的第二维降到了7(sum[][7]),然后又试了下vector存数据,结果都超内存了,重要的是后面发现又是一个沙雕错误:把lower_bound里面的cnt写成了2*n
在这里插入图片描述
后面找博客还发现自己之前的思路有些复杂:cover可以只用记录三种颜色出现的次数,回溯更新覆盖区间长度时 再判断最终的颜色,这样cover的操作会简单很很很多

//cover[root][i]表示区间被i种颜色覆盖次数

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
using namespace std;
//#define int long long
#define LL long long
#define ull unsigned long long
#define pii pair<int,int>
#define mid ((l + r)>>1)
#define chl (root<<1)
#define chr (root<<1|1)
#define lowbit(x) ( x&(-x) )
const int manx = 2e4 + 10;
const int INF = 2e9;
const int mod = 1e4+7;

int color[manx<<2][3];
LL X[manx],sum[manx<<2][8];
//struct node
//{
//    int color[3];
//    LL sum[8];
//}tree[manx<<2];
struct EDGE
{
    LL y,lx,rx;
    int val,color;
    friend bool operator<(const EDGE &a,const EDGE &b){
        return a.y<b.y;
    }
}edge[manx];
void init()
{
    memset(color,0,sizeof color);
    memset(sum,0,sizeof sum);
}
void eval(int root,int l,int r)
{
    int C=0,white=X[r+1]-X[l];
    for(int i=0;i<=2;i++)
        if(color[root][i])C|=(1<<i);
    memset(sum[root],0,sizeof sum[root]);
    if(l==r){
        sum[root][C]=white;
        return;
    }
    for(int i=0;i<=7;i++){
        sum[root][C|i]+=sum[chl][i]+sum[chr][i];
        white-=sum[chl][i]+sum[chr][i];
    }
    sum[root][C]+=white;
}
void change(int root,int l,int r,int ll,int rr,int val,int C)
{
    if(l==ll&&r==rr){
        color[root][C]+=val;
        eval(root,l,r);
        return;
    }
    if(rr<=mid)
        change(chl,l,mid,ll,rr,val,C);
    else if(ll>mid)
        change(chr,mid+1,r,ll,rr,val,C);
    else{
        change(chl,l,mid,ll,mid,val,C);
        change(chr,mid+1,r,mid+1,rr,val,C);
    }
    eval(root,l,r);
}
int main()
{
    int t,n,Cas=0,cou;
    char col[2];
    LL x1,x2,y1,y2;
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s%lld%lld%lld%lld",col,&x1,&y1,&x2,&y2);
            int C;
            if(col[0]=='R')C=0;
            else if(col[0]=='G')C=1;
            else C=2;
            edge[i*2-1]=EDGE{y1,x1,x2,1,C};
            edge[i*2]=EDGE{y2,x1,x2,-1,C};
            X[i*2-1]=x1;
            X[i*2]=x2;
        }
        sort(edge+1,edge+2*n+1);
        sort(X+1,X+2*n+1);
        cou=unique(X+1,X+2*n+1)-X-1;
        LL ans[8]={0};
        for(int i=1;i<2*n;i++){
            int l=lower_bound(X+1,X+cou+1,edge[i].lx)-X;
            int r=lower_bound(X+1,X+cou+1,edge[i].rx)-X-1;
            LL dy=edge[i+1].y-edge[i].y;
            change(1,1,cou,l,r,edge[i].val,edge[i].color);
            if(!dy)continue;
            for(int i=1;i<=7;i++)
                ans[i]+=sum[1][i]*dy;
        }
        printf("Case %d:\n",++Cas);
        printf("%lld\n%lld\n%lld\n%lld\n%lld\n%lld\n%lld\n",ans[1],ans[2],ans[4],ans[3],ans[5],ans[6],ans[7]);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值