hdu 4419 (线段树+线扫描+一点点技巧)

题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=4419
第一次写博客,都点小小的激动,先废话一点。
题意:给你很多个矩形,分别用R G B表示每个矩形三种可能的颜色,重合的的部分为RG RB等颜色,求着七种颜色的面积分别是多少。
题解:最开始想到了用1 2 4分别表示这三种颜色,从而简化颜色的组合,但是始终没想到怎么去更新线段树,最后看了其他的大佬的东西才发现居然还有这种操作。



#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <algorithm>
#include <cmath>
#define qq printf("QAQ\n");
#define ll long long
#define pf(num) printf("%lld\n",num);
using namespace std;
const int maxn=10005;
struct Edge{
 int l,r,f,h;
}edge[maxn<<1];
int pos[maxn<<1];
int num,cnt;
struct tree{
 int l,r,num[8],cover[8],cnt;
}t[maxn<<3];
bool cmp(Edge a,Edge b)
{
 return a.h<b.h;
}
void build(int rt,int l,int r)
{
 for(int i=1;i<8;i++)
 t[rt].cover[i]=0,t[rt].num[i]=0;
 
 t[rt].l=l,t[rt].r=r;
 if(l==r)return ;
 int mid=(l+r)>>1;
 build(rt<<1,l,mid);
 build(rt<<1|1,mid+1,r);
}
void pushup(int rt)
{
 int colour=1*(t[rt].num[1]>0)+2*(t[rt].num[2]>0)+4*(t[rt].num[4]>0);//是些什么颜色 
  //pf(colour);
  for(int i=1;i<8;i++)
  t[rt].cover[i]=0; 
 if(colour>0){
  t[rt].cover[colour]=pos[t[rt].r+1]-pos[t[rt].l];  
  for(int i=1;i<8;i++)
  if(colour!=colour|i){
   int data=0;
   if(t[rt].l!=t[rt].r)
   data=t[rt<<1].cover[i]+t[rt<<1|1].cover[i];
   t[rt].cover[colour|i]+=data;
   t[rt].cover[colour]-=data;
  }
 }
 else if(t[rt].l!=t[rt].r)//memset(t[rt].cover,0,sizeof t[rt].cover) ;
  for(int i=1;i<8;i++)
  t[rt].cover[i]=t[rt<<1].cover[i]+t[rt<<1|1].cover[i];
}
void updata(int rt,int l,int r,int data)
{
// if(l==r)
 if(l<=t[rt].l&&t[rt].r<=r)
 {
  t[rt].num[abs(data)]+=data>0?1:-1;
  pushup(rt);
  return ;
 }
 int mid=(t[rt].l+t[rt].r)>>1;
 if(mid>=r)updata(rt<<1,l,r,data);
 else if(mid<l)updata(rt<<1|1,l,r,data);
 else {
  updata(rt<<1,l,mid,data);
  updata(rt<<1|1,mid+1,r,data);
 }
 pushup(rt);
}
int find(int x)
{
 int l=1,r=cnt;
 while(l<=r)
 {
  int mid=(l+r)>>1;
  if(pos[mid]==x)return mid;
  if(x>pos[mid])l=mid+1;
  else r=mid-1;
 }
}
int main()
{
 int tt,n;
 scanf("%d",&tt);
 for(int ttt=1;ttt<=tt;ttt++)
 {
  scanf("%d",&n);  
  num=n<<1;
  int x1,y1,x2,y2;
  char c;
  for(int i=1;i<=n;i++)
  {
   cin>>c;
   scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
   edge[i].l=x1,edge[i].r=x2,edge[i].h=y1,edge[i].f=(c=='R'?1:(c=='G'?2:4)),pos[i]=x1;
   edge[i+n].l=x1,edge[i+n].r=x2,edge[i+n].h=y2,edge[i+n].f=-(c=='R'?1:(c=='G'?2:4)),pos[i+n]=x2;//用1 2 4分别表示r g b 类比二进制 
  }
  sort(pos+1,pos+num+1);
  sort(edge+1,edge+num+1,cmp);
//  for(int i=1;i<=num;i++)
//  pf(edge[i].h);
  cnt=2;
  for(int i=2;i<=num;i++)
  if(pos[i]!=pos[i-1])pos[cnt++]=pos[i];
  cnt--;
  build(1,1,cnt-1);
  ll ans[8]={0};
  for(int i=1;i<num;i++)
  {
   int l=find(edge[i].l);
   int r=find(edge[i].r)-1;
   updata(1,l,r,edge[i].f);
   for(int j=1;j<8;j++)
   ans[j]+=(ll)t[1].cover[j]*(edge[i+1].h-edge[i].h);
  }
  printf("Case %d:\n",ttt);
  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;
}


    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值