CodeForces 605D Board Game(树状数组+set)

分析:找所有比(a,b)小的数,可以先以找第一个数a比小的所有集合中,第二个数比b小的集合,肯定不能两次sort,可以用线段树+set来搞定这件事情,线段树的坐标为a的值,每个节点存的东西为一个set集合,所有第一个数比a小的集合,然后要找第二个数比b小的集合,只需要在set里面找就好了,这个时候时间复杂度:o(nlognlogn),第一次以为这样会MLE,事实上是不会的,因为算每个集合最多属于多少个set容器,最多属于lognset容器。因此总的空间复杂度为:o(nlogn)



注意使用set的判重函数方式,,

 friend bool operator<(node x,node y)
    {
        if(x.b!=y.b)
          return x.b<y.b;
       else
          return x.id<y.id;

    }


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<set>
#include<queue>
#include<cstring>
#include<stack>

using namespace std;

int a[500005];
int cnt;

struct node
{
    int a,b,c,d;
    int id;
    friend bool operator<(node x,node y)
    {
        if(x.b!=y.b)
          return x.b<y.b;
       else
          return x.id<y.id;

    }
}it[100005];

int vis[100005];

int pre[100005];
int dis[100005];

const int inf=100000000;

int lb(int x)
{
    return x&(-x);
}

set<node>s[400005];

queue<int>q;

void add(int x,int id)
{
    while(x<=400002)
    {
        s[x].insert(it[id]);
        x+=lb(x);
    }
}

int n;

void get(int x,int y,int dist,int pid)
{
    while(x>=1)
    {
        set<node>::iterator itm;
        itm=s[x].begin();
        while(itm!=s[x].end()&&(itm->b)<=y)
        {
            int tid=itm->id;
            if(vis[tid]==1)
            {
                s[x].erase(itm++);
            }
            else
            {
                vis[tid]=1;
                dis[tid]=dist+1;
                pre[tid]=pid;
                q.push(tid);
                s[x].erase(itm++);
            }
        }

        x-=lb(x);
    }
}

void bfs()
{
    int x=0,y=0;
    while(!q.empty())
        q.pop();

    int tx=lower_bound(a,a+cnt,x)-a;
    get(tx+1,y,0,-1);

    if(it[n-1].a==0&&it[n-1].b==0){
        dis[n-1]=1;
        return ;
    }
    while(!q.empty())
    {
        int tid=q.front();
        q.pop();

        if(it[tid].c<=x&&it[tid].d<=y)
            continue;

        if(it[tid].c>=it[n-1].a&&it[tid].d>=it[n-1].b)
        {
            dis[n-1]=dis[tid]+1;
            pre[n-1]=tid;
            return ;
        }

        x=it[tid].c;
        y=it[tid].d;

        tx=lower_bound(a,a+cnt,x)-a;
        get(tx+1,y,dis[tid],tid);

    }
    return;

}

int main()
{

    while(~scanf("%d",&n))
    {
        cnt=0;

        for(int i=0;i<=400002;i++)
            s[i].clear();

        memset(vis,0,sizeof(vis));

        for(int i=0;i<n;i++)
        {
            pre[i]=-1;
            dis[i]=inf;
            scanf("%d%d%d%d",&it[i].a,&it[i].b,&it[i].c,&it[i].d);
            it[i].id=i;
            a[cnt++]=it[i].a;
            a[cnt++]=it[i].b;
            a[cnt++]=it[i].c;
            a[cnt++]=it[i].d;
        }
        a[cnt++]=0;

        sort(a,a+cnt);
        cnt=unique(a,a+cnt)-a;

        for(int i=0;i<n;i++)
        {
            int tx=lower_bound(a,a+cnt,it[i].a)-a;
            add(tx+1,i);   //attention
        }

        bfs();
        if(dis[n-1]==inf)
        {
            printf("-1\n");
        }
        else
        {
            printf("%d\n",dis[n-1]);

            stack<int> ans;

            int tp=n-1;
            while(tp!=-1)
            {
                ans.push(tp);
                tp=pre[tp];
            }

            while(!ans.empty())
            {
                printf("%d ",ans.top()+1);
                ans.pop();
            }

            printf("\n");
        }

    }
}




                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值