BZOJ[3073][Pa2011]Journeys 线段树优化建图+Dijkstra堆优化

传送门ber~

数组开太大会TLE….
会TLE….
TLE…………

咳咳,正常建图的话要一个点一个点那么指
区间指怎么办
可以想到线段树,开两个线段树A,B
线段树A的儿子指父亲,代表想从一个区间出发也可以由包含它的更大的区间出发
线段树B的父亲指儿子,代表能到达区间 l...r l . . . r 的点更能到达其中一小部分点
连边的时候A的点指向一个新的中转点S,T连B的点,S指向T,代价为1
所有B线段树上的点指向A线段树相应位置上的点,代表到达这个点可以接着从这个点出发

注意是无向图

代码如下:

#include<algorithm>
#include<cstring>
#include<ctype.h>
#include<cstdio>
#include<queue>
#define pii pair<int,int>
#define mp make_pair
#define N 9000005
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
struct Node{
    Node *ls,*rs;
    int id,l,r;
    Node():ls(NULL),rs(NULL){}
}*rootS=new Node,*rootT=new Node,pp[N];
priority_queue<pii,vector<pii>,greater<pii> >q;
int fir[N],pos[N],dis[N],belong[N],tot;
bool b[N];
int n,m,p,x,y,x1,y1,top,cnt;
struct Edge{
    int to,nex,k;
    Edge(int _=0,int __=0,int ___=0):to(_),nex(__),k(___){}
}nex[N];
inline void add(int x,int y,int k){
    nex[++top]=Edge(y,fir[x],k);
    fir[x]=top;
}
void maketree(int l,int r,Node *x,Node *y){
    x->l=l;x->r=r;y->l=l;y->r=r;
    x->id=++cnt;y->id=++cnt;
    add(y->id,x->id,0);
    if(l==r){
        pos[l]=x->id;
        return;
    }
    int mid=(l+r)>>1;
    maketree(l,mid,x->ls=&pp[tot++],y->ls=&pp[tot++]);
    maketree(mid+1,r,x->rs=&pp[tot++],y->rs=&pp[tot++]);
    add(x->ls->id,x->id,0);add(x->rs->id,x->id,0);
    add(y->id,y->ls->id,0);add(y->id,y->rs->id,0);
    return;
}
void AddIn(int x,int y,int to,Node *k){
    if(k->l>=x && k->r<=y)
        return add(k->id,to,0);
    int mid=(k->l+k->r)>>1;
    if(mid>=y) AddIn(x,y,to,k->ls);
    else if(mid<x) AddIn(x,y,to,k->rs);
    else AddIn(x,y,to,k->ls),AddIn(x,y,to,k->rs);
    return;
}
void AddOut(int x,int y,int from,Node *k){
    if(k->l>=x && k->r<=y)
        return add(from,k->id,0);
    int mid=(k->l+k->r)>>1;
    if(mid>=y) AddOut(x,y,from,k->ls);
    else if(mid<x) AddOut(x,y,from,k->rs);
    else AddOut(x,y,from,k->ls),AddOut(x,y,from,k->rs);
    return;
}
inline void Dijkstra(){
    memset(dis,0x7f7f7f7f,sizeof dis);
    q.push(mp(dis[pos[p]]=0,pos[p]));
    while(!q.empty()){
        int x=q.top().second;q.pop();
        if(b[x]) continue;
        b[x]=true;
        for(int i=fir[x];i;i=nex[i].nex)
            if(dis[nex[i].to]>dis[x]+nex[i].k){
                dis[nex[i].to]=dis[x]+nex[i].k;
                q.push(mp(dis[nex[i].to],nex[i].to));
            }
    }
}
int main(){
    n=read();m=read();p=read();
    maketree(1,n,rootS,rootT);
    for(int i=1;i<=m;i++){
        x=read();y=read();
        x1=read();y1=read();
        cnt++;
        AddIn(x,y,cnt,rootS);
        add(cnt,cnt+1,1);
        cnt++;
        AddOut(x1,y1,cnt,rootT);
        cnt++;
        AddIn(x1,y1,cnt,rootS);
        add(cnt,cnt+1,1);
        cnt++;
        AddOut(x,y,cnt,rootT);
    }
    Dijkstra();
    for(int i=1;i<=n;i++)
        printf("%d\n",dis[pos[i]]);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值