[CERC2017]Buffalo Barricades

这个题目,扫描线+玄学**
大概操作就是用个扫描线从上往下扫。
博主有点懒,就直接贴代码了,但是我还是给大家贴个比较详细的博客,除了代码都可以看wym的博客,我基本上就是按wym大佬的思路来的,当然,我的代码里也加了点注释,大家也请凑合着看吧。

Ps:
ycz:STL什么辣鸡,跑这么慢,你看手写splay多么快
wym:STL有啥不好啦,代码量短,如果手写splay还要各种操作,STL多简洁明了啊

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)     print(x/10);
    putchar(x%10+'0');
}
const int N=3e5;
int Ans[N+10],last[N+10],cnt[N+10];
struct S1{
    int x,y,t;
    void join(int i){x=read(),y=read(),t=i;}
    bool operator <(const S1 &a)const{return y!=a.y?y>a.y:x<a.x;}
}A[N*2+10];
struct S2{
    //splay的维护需要按照x坐标为第一关键字,时间为第二关键字来维护 
    #define ls(x) tree[x][0]
    #define rs(x) tree[x][1]
    #define T(x) (rs(f[x])==x)
    struct S3{
        int x,t;
        void clear(){x=t=0;}
        void join(int a,int b){x=a,t=b;}
        bool operator <(const S3 &a)const{return x!=a.x?x<a.x:t<a.t;}
    }val[N*4+10],tmp;
    int f[N*4+10],tree[N*4+10][2];
    int root,len;
    void clear(int x){ls(x)=rs(x)=f[x]=0,val[x].clear();}
    void move(int x){
        int fa=f[x],son=tree[x][T(x)^1];
        tree[x][T(x)^1]=fa;
        tree[fa][T(x)]=son;
        if (son)    f[son]=fa;
        f[x]=f[fa];
        if (f[x])   tree[f[x]][T(fa)]=x;
        f[fa]=x;
    }
    void splay(int x){
        while (f[x]){
            if (f[f[x]])    T(x)==T(f[x])?move(f[x]):move(x);
            move(x);
        }
        root=x;
    }
    void insert(int x,int t){
        val[++len].join(x,t);
        if (!root){
            root=len;
            return;
        }
        int i=root;
        while (true){
            if (val[len]<val[i]){
                if (!ls(i)){f[ls(i)=len]=i;break;}
                i=ls(i);
            }else{
                if (!rs(i)){f[rs(i)=len]=i;break;}
                i=rs(i);
            }
        }
        splay(len);
    }
    int get_pre(){
        int x=ls(root);
        while (rs(x))   x=rs(x);
        return x;
    }
    int get_suc(){
        int x=rs(root);
        while (ls(x))   x=ls(x);
        return x;
    }
    void Delete(int x){
        splay(x);
        if (!(ls(x)&&rs(x))){
            f[root=ls(x)+rs(x)]=0;
            clear(x);
            return;
        }
        //删除必须找后继节点,这样才能保证根不变 
        int i=get_suc();
        splay(i);
        f[ls(i)=ls(x)]=i;
        clear(x);
    }
}Splay;
struct S4{
    int f[N+10];
    int find(int x){return f[x]?f[x]=find(f[x]):x;}
    void merge(int x,int y){
        x=find(x),y=find(y);
        if (x!=y)   f[x]=y,cnt[y]+=cnt[x];
        //merge有顺序 
    }
}F;
int main(){
    int n=read();
    for (int i=1;i<=n;i++){
        A[i].join(0);
        (A[i].x<<=1)--;
        (A[i].y<<=1)--;
    }
    int m=read();
    for (int i=1;i<=m;i++){
        A[i+n].join(i);
        A[i+n].x<<=1;
        A[i+n].y<<=1;
    }
    //消去0.5的影响,所以位移一位 
    sort(A+1,A+1+n+m);
    for (int i=1;i<=n+m;i++){
        Splay.insert(A[i].x,A[i].t);
        //因为if语句里两个都要insert,虽然用处不同,但是我懒 
        if (A[i].t){
            int suc=Splay.get_suc();
            if (suc)    last[A[i].t]=Splay.val[suc].t;
            //记录右边的第一个时间比其大的点,方便并查集维护 
            while (true){
                //保证根不变,一个个删掉前驱节点,类似于单调栈
                int pre=Splay.get_pre();
                if (!pre||Splay.val[pre].t<A[i].t)  break;
                Splay.Delete(pre);
            }
        }else{
            //insert之后才能找到其后继,但是这个点不能加进去,所以直接删掉 
            int T=Splay.get_suc();
            Splay.Delete(Splay.root);
            if (T)  cnt[Splay.val[T].t]++;
            //初步统计答案 
        }
    }
    for (int i=m;i;i--){
        Ans[i]=cnt[F.find(i)];
        if (last[i])    F.merge(i,last[i]);
        //merge操作有顺序,如果说last[i]的时间大于i的时间,加不加没有任何影响,为了方便,都加 
    }
    for (int i=1;i<=m;i++)  printf("%d\n",Ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/Wolfycz/p/8497097.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值