此题需要了解线段树基础知识
还是建边难!复杂度O(n^2)!!!
而这道题涉及到:v->[l,r] 或是 [l,r]->v 等此类区间建边
我们想到了线段树可以轻松将区间问题 降解为log级别
如下图,我们构建两颗线段树
一颗 出树,一颗 入树
入树主要负责接受信息,出树主要负责引出信息~
1.为了传递信息,如上图的边建好边权设为0
2.若从v->[l,r]: 则在出树中找到v所在树上点编号[叶子结点处],入树中我们把[l,r]拆解成几个小区间,如下图:
3.同理若从[l,r]->v:反之,还是举个例子:
4.还没结束,为了保证图(及线段树)的连通性,我们的两棵树的叶子之间连边,边权为0。
所以这样就over
注意:
1.连边要连线段树结点编号,
2.方便的话使用偏移量m,(入树 [1,m] ,出树 [m+1,2*m] )
以梦为码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int M=5e6+5;
typedef long long ll;
int nxt[M],to[M],head[M],a[N],tot,m;
ll len[M],inf;
struct segmentree { //第一棵树是入数,第二颗树是出树
int l,r;
}T[N*9];
void add_edge(int u,int v,ll w) {
tot++; nxt[tot]=head[u]; to[tot]=v; len[tot]=w; head[u]=tot;
}
void Build_tree(int i,int l,int r,int ad) {
T[i+ad].l=l; T[i+ad].r=r;
if(!ad) {
m=max(m,i);
if(l==r) {a[l]=i;return;}
}
else if(l==r) {
add_edge(i,i+ad,0); //两个图的叶结点相通
return;
}
int mid=(l+r)>>1;
Build_tree(i*2,l,mid,ad); Build_tree(i*2+1,mid+1,r,ad);
if(!ad) {add_edge(i,i*2,0); add_edge(i,i*2+1,0);}
else {add_edge(i*2+ad,i+ad,0); add_edge(i*2+1+ad,i+ad,0);}
}
void one_more(int i,int l,int r,int v,int w) {
if(l<=T[i].l&&T[i].r<=r) {
add_edge(v+m,i,w);
return;
}
int mid=(T[i].l+T[i].r)>>1;
if(l<=mid) one_more(i*2,l,r,v,w);
if(r>mid) one_more(i*2+1,l,r,v,w);
}
void more_one(int i,int l,int r,int v,int w) {
if(l<=T[i].l&&T[i].r<=r) {
add_edge(i+m,v,w);
return;
}
int mid=(T[i].l+T[i].r)>>1;
if(l<=mid) more_one(i*2,l,r,v,w);
if(r>mid) more_one(i*2+1,l,r,v,w);
}
ll dis[M];
struct node {
int p; ll w;
bool operator<(const node &u) const{
return w>u.w;
}
};
bool mark[M];
priority_queue<node> Q;
void DJ(int s) {
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
Q.push((node){s,0});
while(!Q.empty()) {
int u=Q.top().p; Q.pop();
if(mark[u]) continue;
mark[u]=true;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(!mark[v]&&dis[v]>dis[u]+len[i]) {
dis[v]=dis[u]+len[i];
Q.push((node){v,dis[v]});
}
}
}
}
int main() {
int n,q,s;
scanf("%d%d%d",&n,&q,&s);
Build_tree(1,1,n,0);
Build_tree(1,1,n,m);
for(int i=1;i<=q;i++) {
int op,u,v,l,r;ll w;
scanf("%d",&op);
if(op==1) {
scanf("%d%d%lld",&u,&v,&w);
add_edge(a[u]+m,a[v],w);
}
else if(op==2) {
scanf("%d%d%d%lld",&v,&l,&r,&w);
one_more(1,l,r,a[v],w);
}
else {
scanf("%d%d%d%lld",&v,&l,&r,&w);
more_one(1,l,r,a[v],w);
}
}
DJ(a[s]);
inf=dis[0];
for(int i=1;i<=n;i++) {
if(dis[a[i]]==inf) printf("-1 ");
else printf("%lld ",dis[a[i]]);
}
return 0;
}
综上,只需要q*log(n)条边,因为任意一个区间 [l,r] 最多划分为2log(n)个小区间
留一道类似思考题:
戳我 P6348 [PA2011]Journeys