bzoj3228 [ SDOI2008 ] -- 扫描线 + 线段树

首先曼哈顿距离有如下性质:

|x1x2|+|y1y2|=max(|x1y1(x2y2)|,|x1+y1(x2+y2)|)

于是将点 (x,y) 的坐标转化为 (xy,x+y) ,点控制的范围就变成了一个正方形,扫描线+线段树即可。
注意转化后的坐标系中 x+y 为奇数的点是不存在的,所以线段树还要维护奇数的个数。

代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
#define N 100010
#define M 5000000
#define INF 200000000
#define ll long long
struct Line{
    int w,f;
    bool d;
}b[N<<1];
struct Node{
    int x,y,w;
}a[N];
long long Ans;
int i,j,k,n,m,t,num,T,cnt,c[M],C[M],cl[M],cr[M],p[M],L[N],R[N],Rt,m1,m2,M1,M2;
int d[N];
inline bool Cmp(Line a,Line b){
    return a.w<b.w;
}
inline void Update(int& x,int l,int r,int L,int R,int y){
    if(l>R||r<L)return;
    if(!x)x=++cnt;
    if(l>=L&&r<=R){
        p[x]+=y;
        c[x]=(p[x]>0?r-l+1:c[cl[x]]+c[cr[x]]);
        C[x]=(p[x]>0?(r-l+1>>1)+((l%2&&r%2)?1:0):C[cl[x]]+C[cr[x]]);
        return;
    }
    int Mid=l+r>>1;
    Update(cl[x],l,Mid,L,R,y);Update(cr[x],Mid+1,r,L,R,y);
    c[x]=(p[x]>0?r-l+1:c[cl[x]]+c[cr[x]]);
    C[x]=(p[x]>0?(r-l+1>>1)+((l%2&&r%2)?1:0):C[cl[x]]+C[cr[x]]);
}
inline bool Cmp1(Node a,Node b){
    return a.y<b.y;
}
inline ll Get(int a,int b,int c,int d){
    if(a>=b)return 0;
    c=max(c,0);d=max(d,0);
    if(a==b-1)return max(c,d-1);
    if(!c&&!d)return 0;
    if(!c)return d<b-a?1ll*d*(d-1)/2:1ll*(2*d-b+a-1)*(b-a)/2;
    if(!d)return c<b-a?1ll*c*(c+1)/2:1ll*(2*c-b+a+1)*(b-a)/2;
    if(c+d<=b-a+1)return 1ll*c*(c+1)/2+1ll*d*(d-1)/2;
    int x=c-d+b-a>>1;
    if(x<0)return 1ll*(2*d-b+a-1)*(b-a)/2;
    if(x>=b-a-1)return 1ll*(2*c-b+a+1)*(b-a)/2;
    if(x*2==c-d+b-a)return 1ll*(c*2-x+1)*x/2+1ll*(d*2-b+a+x-1)*(b-a-x)/2;
    return 1ll*(c*2-x+1)*x/2+1ll*(d*2-b+a+x-1)*(b-a-x)/2+1;
}
inline void Solve1(){
    sort(a+1,a+k+1,Cmp1);
    d[k]=a[k].w-n+a[k].x-a[k].y;
    for(i=k-1;i;i--)d[i]=max(d[i+1],a[i].w-n+a[i].x-a[i].y);
    t=-INF;d[k+1]=-INF;a[0].y=M1;a[k+1].y=M2;
    for(i=0;i<=k;i++){
        if(i)t=max(t,a[i].w-n+a[i].x+a[i].y);
        Ans-=Get(i?a[i].y:M1,i<k?a[i+1].y:M2,t-a[i].y,d[i+1]+a[i+1].y);
    }
    if(t-M2>0)Ans-=t-M2;
    d[k]=a[k].w+1-a[k].x-a[k].y;
    for(i=k-1;i;i--)d[i]=max(d[i+1],a[i].w+1-a[i].x-a[i].y);
    t=-INF;
    for(i=0;i<=k;i++){
        if(i)t=max(t,a[i].w+1-a[i].x+a[i].y);
        Ans-=Get(i?a[i].y:M1,i<k?a[i+1].y:M2,t-a[i].y,d[i+1]+a[i+1].y);
    }
    if(t-M2>0)Ans-=t-M2;
}
inline void Solve2(){
    sort(a+1,a+k+1,Cmp1);
    d[k]=a[k].w-n+a[k].x-a[k].y;
    for(i=k-1;i;i--)d[i]=max(d[i+1],a[i].w-n+a[i].x-a[i].y);
    t=-INF;d[k+1]=-INF;a[0].y=1;a[k+1].y=m;
    for(i=0;i<=k;i++){
        if(i)t=max(t,a[i].w-n+a[i].x+a[i].y);
        Ans-=Get(i?a[i].y:1,i<k?a[i+1].y:m,t-a[i].y,d[i+1]+a[i+1].y);
    }
    if(t-m>0)Ans-=t-m;
    d[k]=a[k].w+1-a[k].x-a[k].y;
    for(i=k-1;i;i--)d[i]=max(d[i+1],a[i].w+1-a[i].x-a[i].y);
    t=-INF;
    for(i=0;i<=k;i++){
        if(i)t=max(t,a[i].w+1-a[i].x+a[i].y);
        Ans-=Get(i?a[i].y:1,i<k?a[i+1].y:m,t-a[i].y,d[i+1]+a[i+1].y);
    }
    if(t-m>0)Ans-=t-m;
}
int main(){
    Read(n);Read(m);Read(k);
    m1=M1=INF;m2=M2=-INF;
    for(i=1;i<=k;i++){
        Read(a[i].x);Read(a[i].y);Read(a[i].w);
        a[i].w=min(a[i].w,INF);
        M1=min(M1,a[i].y-a[i].w);M2=max(M2,a[i].y+a[i].w);
        t=a[i].x,a[i].x-=a[i].y,a[i].y+=t;
        L[i]=a[i].y-a[i].w;R[i]=a[i].y+a[i].w;
        b[++num].f=i;b[num].w=a[i].x-a[i].w;
        b[++num].f=i;b[num].w=a[i].x+a[i].w+1;b[num].d=1;
        m1=min(m1,L[i]);m2=max(m2,R[i]);
    }
    sort(b+1,b+num+1,Cmp);
    for(i=1;i<=num;i=j+1){
        for(j=i;j<=num&&b[j+1].w==b[j].w;){
            Update(Rt,m1,m2,L[b[j].f],R[b[j].f],b[j].d?-1:1);
            j++;
        }
        if(j<=num)Update(Rt,m1,m2,L[b[j].f],R[b[j].f],b[j].d?-1:1);
        if(j<num){
            Ans+=1ll*(b[j+1].w-b[i].w)/2*c[Rt];
            if(!(b[i].w%2)&&b[j+1].w%2)Ans+=c[Rt]-C[Rt];
            if((b[i].w%2)&&!(b[j+1].w%2))Ans+=C[Rt];
        }
    }
    for(i=1;i<=k;i++)t=a[i].x,a[i].x=a[i].x+a[i].y>>1,a[i].y=a[i].y-t>>1;
    Solve1();
    for(i=1;i<=k;i++)swap(a[i].x,a[i].y);
    swap(n,m);
    Solve2();
    cout<<Ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值