【PA2013】【BZOJ3839】Działka

Description

平面上有n个不重复的点。每次询问一个边平行坐标轴的矩形内(包含边界)的点组成的凸包的面积。
Input

第一行两个整数k,n(1<=k<=1000000,3<=n<=3000)。
接下来n行,每行两个整数x_i,y_i(0<=x_i,y_i<=k),表示点的坐标。
接下来一行一个整数m(1<=m<=1000000),表示询问数量。
接下来m行,每行四个整数a,b,c,d(0<=a< b<=k,0<=c< d<=k),表示询问的矩形范围为a<=x<=b,c<=y<=d。
Output

对于每个询问输出一行表示面积。保留小数点后一位。
Sample Input

9 7

1 1

1 3

3 3

3 1

6 5

6 6

7 3

3

0 4 0 4

2 7 0 7

3 7 3 6
Sample Output

4.0

10.0

6.0
HINT

Source

150s时限,卡评测好题
考虑把要求的凸包拆分成四部分:左上,左下,右上,右下
分别求每个点对按这四个方向构建凸包的面积(叉积)
对每个询问用线段树+扫描线把叉积累积起来求和然后除以二就是答案
询问限定的那四个范围没法直接处理,所以拆分询问+容斥
这题好厉害…%Claris…

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 3010
#define GET (ch>='0'&&ch<='9')
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
#define ln rt<<1
#define rn rt<<1|1
#define SIZE 1000010
#define LL long long
using namespace std;
int k,n,m;
LL ans;
int sta[MAXN],top;
inline void in(int &x)
{
    char ch=getchar();x=0;
    while (!GET)    ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();
}
LL f[5][MAXN][MAXN];
int que[5][SIZE];
struct Point
{
    int x,y,id;
    inline friend Point operator -(Point a,Point b) {return (Point){a.x-b.x,a.y-b.y};}
    inline friend LL operator *(Point a,Point b)    {return (LL)a.x*b.y-(LL)a.y*b.x;}
}s[MAXN];
inline LL cross(Point a,Point b,Point c)    {Point A=b-a,B=c-b;return B*A;}
inline bool cmp1(Point a,Point b)   {return a.x==b.x?a.y<b.y:a.x<b.x;}
inline bool cmp2(Point a,Point b)   {return a.x==b.x?a.y>b.y:a.x<b.x;}
inline bool cmp3(Point a,Point b)   {return a.x<b.x;}
inline bool cmp4(Point a,Point b)   {return a.y<b.y;}
int vis[MAXN],pos;
struct seg  {int l,r,x;}tree[SIZE<<2];
inline void build(int rt=1,int l=0,int r=k)
{
    tree[rt].x=0;tree[rt].l=l;tree[rt].r=r;int mid=(l+r)>>1;
    if (l==r)   return; build(lchild);build(rchild);
}
inline void modify(int rt,int x,int delta)
{
    int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1; tree[rt].x=delta;
    if (L==R)   return;
    if (x<=mid) modify(ln,x,delta); else    modify(rn,x,delta);
}
inline int merge(int x,int y)   {return vis[x]>vis[y]?x:y;}
inline int query(int rt,int l,int r)
{
    int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1;
    if (l<=L&&r>=R) return tree[rt].x;
    if (r<=mid) return query(ln,l,r);
    if (l>mid)  return query(rn,l,r);
    return merge(query(ln,l,mid),query(rn,mid+1,r));
}
struct Query
{
    int a,b,c,d;
    inline bool operator <(const Query& x)const {return a<x.a;}
}q[SIZE],newq[SIZE];
int main()
{
    in(k);in(n);
    for (int i=1;i<=n;i++)  in(s[i].x),in(s[i].y),s[i].id=i;
    sort(s+1,s+n+1,cmp1);
    for (int i=2;i<=n;i++)//A 
    {
        top=0;sta[top]=i;int l=s[sta[top]].id;
        for (int j=i-1;j;j--)
            if (s[j].y<=s[i].y)
            {
                while (top&&cross(s[j],s[sta[top]],s[sta[top-1]])<0)    top--;
                f[1][l][s[j].id]=f[1][l][s[sta[top]].id]+s[sta[top]]*s[j];sta[++top]=j;
            }
    }
    sort(s+1,s+n+1,cmp2);
    for (int i=1;i<n;i++)//B 
    {
        top=0;sta[top]=i;int l=s[sta[top]].id;
        for (int j=i+1;j<=n;j++)
            if (s[j].y<=s[i].y)
            {
                while (top&&cross(s[sta[top-1]],s[sta[top]],s[j])<0)    top--;
                f[2][l][s[j].id]=f[2][l][s[sta[top]].id]+s[sta[top]]*s[j];sta[++top]=j;
            }
    }
    for (int i=2;i<=n;i++)//C 
    {
        top=0;sta[top]=i;int l=s[sta[top]].id;
        for (int j=i-1;j;j--)
            if (s[j].y>=s[i].y)
            {
                while (top&&cross(s[j],s[sta[top]],s[sta[top-1]])>0)    top--;
                f[3][l][s[j].id]=f[3][l][s[sta[top]].id]+s[sta[top]]*s[j];sta[++top]=j;
            }
    }
    sort(s+1,s+n+1,cmp1);
    for (int i=1;i<n;i++)//D 
    {
        top=0;sta[top]=i;int l=s[sta[top]].id;
        for (int j=i+1;j<=n;j++)
            if (s[j].y>=s[i].y)
            {
                while (top&&cross(s[sta[top-1]],s[sta[top]],s[j])>0)    top--;
                f[4][l][s[j].id]=f[4][l][s[sta[top]].id]+s[sta[top]]*s[j];sta[++top]=j;
            }
    }
    in(m);
    for (int i=1;i<=m;i++)  in(q[i].a),in(q[i].b),in(q[i].c),in(q[i].d);
    for (int i=1;i<=m;i++)  newq[i]=(Query){q[i].d,q[i].a,q[i].b,i};//考虑用容斥得到询问答案  
    sort(s+1,s+n+1,cmp4);sort(newq+1,newq+m+1);int i,j;
    for (build(1,0,k),i=j=1;i<=m;i++)
    {
        while (j<=n&&s[j].y<=newq[i].a) modify(1,s[j].x,s[j].id),vis[s[j++].id]=++pos;
        que[1][newq[i].d]=query(1,newq[i].b,newq[i].c);
    }
    for (int i=1;i<=m;i++)  newq[i]=(Query){q[i].c,q[i].a,q[i].b,i};
    sort(s+1,s+n+1,cmp4);sort(newq+1,newq+m+1);
    for (build(1,0,k),i=m,j=n;i;i--)
    {
        while (j&&s[j].y>=newq[i].a)    modify(1,s[j].x,s[j].id),vis[s[j--].id]=++pos;
        que[3][newq[i].d]=query(1,newq[i].b,newq[i].c);
    }
    for (int i=1;i<=m;i++)  newq[i]=(Query){q[i].b,q[i].c,q[i].d,i};
    sort(s+1,s+n+1,cmp3);sort(newq+1,newq+m+1);
    for (build(1,0,k),i=j=1;i<=m;i++)
    {
        while (j<=n&&s[j].x<=newq[i].a) modify(1,s[j].y,s[j].id),vis[s[j++].id]=++pos;
        que[4][newq[i].d]=query(1,newq[i].b,newq[i].c);
    }
    for (int i=1;i<=m;i++)  newq[i]=(Query){q[i].a,q[i].c,q[i].d,i};
    sort(s+1,s+n+1,cmp3);sort(newq+1,newq+m+1);
    for (build(1,0,k),i=m,j=n;i;i--)
    {
        while (j&&s[j].x>=newq[i].a)    modify(1,s[j].y,s[j].id),vis[s[j--].id]=++pos;
        que[2][newq[i].d]=query(1,newq[i].b,newq[i].c);
    }
    for (int i=1;i<=m;i++)
    {
        ans=f[1][que[1][i]][que[2][i]]-f[2][que[1][i]][que[4][i]]-f[3][que[3][i]][que[2][i]]+f[4][que[3][i]][que[4][i]];
        printf("%lld.%d\n",ans>>1,ans&1ll?5:0);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值