bzoj1807: [Ioi2007]Pairs 彼此能听得见的动物对数

传送门
首先题目明摆着让你分类讨论骗分
如果是1维直接Two pointer扫一遍
对于2维我们转换坐标系之后扫描线+树状数组维护。
对于3维我们按照2维的做法转换坐标系之后暴力求这个点能听到的动物个数,注意答案要除2.

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 300300
using namespace std;
int n,m,thh,D;
long long ans;
namespace task1{
    int a[N];
    inline void solve(){
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        for (int i=1,j=1;i<=n;i++){
            while (a[i]-a[j]>D) j++;
            ans+=i-j;
        }
        printf("%lld",ans);
    }
}
namespace task2{
    int tot=0,x,y,X,Y;
    int c[N],b[N];
    struct P{
        int x,y,z,t;
        P(){}
        P(int _x,int _y,int _z,int _t):
            x(_x),y(_y),z(_z),t(_t){}
    }a[N];
    inline bool cmp(const P& a,const P& b){
        return a.x==b.x?a.t<b.t:a.x<b.x;
    }
    inline int ef(int x){
        int l=1,r=tot,mid,ans;
        while (l<=r){
            mid=(l+r)/2;
            if (b[mid]<=x) ans=mid,l=mid+1;
            else r=mid-1;
        }
        return ans;
    }
    inline void add(int x,int y){
        for (;x<=tot;x+=x&(-x)) c[x]+=y;
    }
    inline int ask(int x){
        int s=0;
        for (;x;x-=x&(-x)) s+=c[x];
        return s;
    }
    inline void solve(){
        for (int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            X=x+y; Y=x-y;
            a[++tot]=P(X,Y,0,i); b[tot]=Y;
            a[++tot]=P(X+D,Y-D,Y+D,n+1); b[tot]=Y-D;
            a[++tot]=P(X-D,Y-D,Y+D,0); b[tot]=Y+D; 
        }
        sort(a+1,a+tot+1,cmp);
        sort(b+1,b+tot+1);
        for (int i=1;i<=tot;i++)
            if (a[i].t&&a[i].t<=n) ans+=ask(ef(a[i].y));
            else{
                a[i].y=ef(a[i].y); a[i].z=ef(a[i].z)+1;
                if (a[i].t) add(a[i].y,-1),add(a[i].z,1);
                else add(a[i].y,1),add(a[i].z,-1);
            }
        ans=(ans-n)/2;
        printf("%lld",ans);
    }
}
namespace task3{
    int x,y,z,X,Y;
    int a[N][3],s[76][152][152];
    inline int ask(int j,int x,int y){
        return s[j][max(min(x,150),0)][max(min(y,150),0)];
    }
    inline void solve(){
        for (int i=1;i<=n;i++){
            scanf("%d%d%d",&x,&y,&z);
            X=y+z; Y=y-z+75;
            a[i][0]=x; a[i][1]=X; a[i][2]=Y;
            s[x][X][Y]++;
        }
        for (int i=1;i<=75;i++)
            for (int j=1;j<=150;j++)
                for (int k=1;k<=150;k++)
                    s[i][j][k]+=s[i][j-1][k]+s[i][j][k-1]-s[i][j-1][k-1];
        for (int i=1;i<=n;i++)
            for (int j=1;j<=75;j++)
                if (abs(a[i][0]-j)<=D){
                    x=D-abs(a[i][0]-j);
                    ans+=ask(j,a[i][1]+x,a[i][2]+x)
                        -ask(j,a[i][1]-x-1,a[i][2]+x)
                        -ask(j,a[i][1]+x,a[i][2]-x-1)
                        +ask(j,a[i][1]-x-1,a[i][2]-x-1);
                }
        ans=(ans-n)/2;
        printf("%lld",ans);
    }
}
int main(){
    scanf("%d%d%d%d",&m,&n,&D,&thh);
    if (m==1) task1::solve();
    if (m==2) task2::solve();
    if (m==3) task3::solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值