bzoj3073(线段树优化建图)

汪聚聚的题真的是骚。。。直接讲套路

建2棵线段树一棵入树、一棵出树,叶子节点分别连边,然后连边的时候可以按照线段树的方式将区间分成logn个区间,在区间之间直接连边,但是会有log^2条还是很糟,因此对条单向边建一个新节点,从入树和出树分别想这个节点连边,然后就只有logn条边了。。由于是跑最短路我们就只用线段树的下标表示思想而不直接写线段树,跑起来还是挺快的。。

建完图跑spfa美滋滋。。

/**
 *          ┏┓    ┏┓
 *          ┏┛┗━━━━━━━┛┗━━━┓
 *          ┃       ┃  
 *          ┃   ━    ┃
 *          ┃ >   < ┃
 *          ┃       ┃
 *          ┃... ⌒ ...  ┃
 *          ┃              ┃
 *          ┗━┓          ┏━┛
 *          ┃          ┃ Code is far away from bug with the animal protecting          
 *          ┃          ┃   神兽保佑,代码无bug
 *          ┃          ┃           
 *          ┃          ┃        
 *          ┃          ┃
 *          ┃          ┃           
 *          ┃          ┗━━━┓
 *          ┃              ┣┓
 *          ┃              ┏┛
 *          ┗┓┓┏━━━━━━━━┳┓┏┛
 *           ┃┫┫       ┃┫┫
 *           ┗┻┛       ┗┻┛
 */
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1LL<<(x))
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 6000005
#define nm 50000005 
#define N 1000005
#define M(x,y) x=max(x,y)
const double pi=acos(-1);
const ll inf=19260817;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}



struct edge{int t,v;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y,int v){o->t=y;o->v=v;o->next=h[x];h[x]=o++;}
int n,m,_x,_y,tot,cnt,d[NM],S,b[NM],_t,__x,__y;
queue<int>q;
bool v[NM];

void build(int i,int x,int y){
    if(x==y){add(i+cnt,i,0);b[x]=i;return;}
    build(i<<1,x,mid);build(i<<1|1,mid+1,y);
    add(i<<1,i,0);add(i<<1|1,i,0);
    add(cnt+i,cnt+(i<<1),0);add(cnt+i,cnt+(i<<1|1),0);
}

void mod(int i,int x,int y){
    if(_y<x||y<_x)return;
    if(_x<=x&&y<=_y){if(_t)add(i,tot,1);else add(tot,i+cnt,1);return;}
    mod(i<<1,x,mid);mod(i<<1|1,mid+1,y);
}

void spfa(){
    q.push(S);v[S]++;inc(i,1,tot)d[i]=inf;d[S]=0;
    while(!q.empty()){
	int t=q.front();q.pop();v[t]=false;
	link(t)if(d[j->t]>d[t]+j->v){
	    d[j->t]=d[t]+j->v;
	    if(!v[j->t])v[j->t]++,q.push(j->t);
	}
    }
}

int main(){
    freopen("data.in","r",stdin);
    n=read();m=read();S=read();
    for(cnt=1;cnt<=2*n;cnt<<=1);
    build(1,1,n);
    S=b[S];
    tot=cnt<<1;
    while(m--){
	tot++;
	_x=read();_y=read();__x=read();__y=read();_t=1;
	mod(1,1,n);swap(__x,_x);swap(__y,_y);_t=0;mod(1,1,n);
	tot++;_t=1;
	mod(1,1,n);swap(__x,_x);swap(__y,_y);_t=0;mod(1,1,n);
    }
    spfa();
    //inc(i,1,tot)printf("%d ",d[i]);putchar('\n');
    inc(i,1,n)printf("%d\n",d[b[i]]/2);
    return 0;
}

3073: [Pa2011]Journeys

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 639  Solved: 192
[Submit][Status][Discuss]

Description

Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路。N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a,b),(c,d)表示,对于任意两个国家x,y,如果a<=x<=b,c<=y<=d,那么在xy之间建造一条道路。Seter保证一条道路不会修建两次,也保证不会有一个国家与自己之间有道路。

Seter好不容易建好了所有道路,他现在在位于P号的首都。Seter想知道P号国家到任意一个国家最少需要经过几条道路。当然,Seter保证P号国家能到任意一个国家。

 

注意:可能有重边

Input

第一行三个数N,M,P。N<=500000,M<=100000。

后M行,每行4个数A,B,C,D。1<=A<=B<=N,1<=C<=D<=N。

 

Output

N行,第i行表示P号国家到第i个国家最少需要经过几条路。显然第P行应该是0。

 

 

Sample Input

5 3 4
1 2 4 5
5 5 4 4
1 1 3 3

 

Sample Output

1
1
2
0
1

HINT

Source

seter翻译

[Submit][Status][Discuss]



猜你喜欢

转载自blog.csdn.net/qkoqhh/article/details/81710852
今日推荐