http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3724
这是道离线的好题!
首先,关于题意:现在有一个从1~n的n-1条有向边,其中i连向i+1,并且给出了这n-1条边的长度。除了这n-1条边之外,还有m条特殊的有向边。现在规定,特殊的边最多只能走一次。然后给出q组询问,问从u到v的最短路。
其中n<=1e5,m<=2e5,q<=2e5。边权值都是正的
直接在线求我真不是很清楚怎么做,也许根本做不到。。。我是离线做的:
观察询问,<u,v>。这其中v可能是大于u,也可能是小于u的,即一个是往后走,一个是往回走。(对于u==v直接就是0了。。。。)
情况一:对于v>u的情况,我们只要找到u->v之间是否存在一个特殊边x->y,u<=x<y<=v,使总路径更短
情况二:对于v<u的情况,我们必须在u点或u后面找到一条往回走的一条特殊边x->y,且x>=u,y<=v。
定义sum[i]表示i点到n点的长度和
以上两种情况的答案都可表示为:ans[u,v]=sum[u]-sum[v]+min(sum[y]-sum[x]+Wxy)
我们可以将这两种情况分开处理:
情况一:将询问按u,从大到小排序,然后从大到小枚举u点,将起点在u点上的向后的特殊边<x,y>(x==u&&y>x)插入到线段树中的y点上,权值为sum[y]-sum[x]+Wxy。然后处理在u点上的询问<u,v>,即ans[u,v]=sum[u]-sum[v]+min(u点到v点的最小值,0)。注意往后走的时候可以不走特殊边的!
情况二:将询问按v,从小到大排序,然后从小到大枚举v点,将终点在v点上的向前的特殊边<x,y>(y==v&&x<y)插入到线段树中的x点上,权值为sum[y]-sum[x]+Wxy。然后处理在v点上的询问<u,v>,即ans[u,v]=sum[u]-sum[v]+u到n点的最小值。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100010;
typedef long long ll;
const ll INF = 1e16;
int n,m;
ll sum[maxn];
struct Query{
int id,u,v;
void set(int u,int v,int id){
this->id=id;
this->u=u;
this->v=v;
}
}q1[maxn*2],q2[2*maxn];
bool cmp1(const Query &a,const Query &b){
return a.u>b.u;
}
bool cmp2(const Query &a,const Query &b){
return a.v<b.v;
}
int top1,top2,top11,top22,node1[maxn],node2[maxn];
ll ans[maxn*2];
struct Side{
int to,next;
ll w;
}side1[200010],side2[200010];
void add_side1(int u,int v,ll w){
side1[top11]=(Side){v,node1[u],w};
node1[u]=top11++;
}
void add_side2(int u,int v,ll w){
side2[top22]=(Side){v,node2[u],w};
node2[u]=top22++;
}
ll val[maxn*4],lazy[maxn*4];
void build(int th,int l,int r){
val[th]=INF;
lazy[th]=INF;
if(l==r)return;
int mid=(l+r)/2;
build(th*2,l,mid);
build(th*2+1,mid+1,r);
}
void insert(int th,int x,int L,int R,ll v){
if(x==L&&x==R){
val[th]=min(val[th],v);
lazy[th]=min(lazy[th],v);
return;
}
int mid=(L+R)/2;
if(lazy[th]!=INF){
lazy[th*2]=min(lazy[th],lazy[th*2]);
val[th*2]=min(val[th*2],lazy[th]);
lazy[th*2+1]=min(lazy[th],lazy[th*2+1]);
val[th*2+1]=min(val[th*2+1],lazy[th]);
lazy[th]=INF;
}
if(x<=mid){
insert(th*2,x,L,mid,v);
}else{
insert(th*2+1,x,mid+1,R,v);
}
val[th]=min(val[th*2],val[th*2+1]);
}
ll query(int th,int l,int r,int L,int R){
if(l==L&&r==R){
return val[th];
}
if(lazy[th]!=INF){
lazy[th*2]=min(lazy[th],lazy[th*2]);
val[th*2]=min(val[th*2],lazy[th]);
lazy[th*2+1]=min(lazy[th],lazy[th*2+1]);
val[th*2+1]=min(val[th*2+1],lazy[th]);
lazy[th]=INF;
}
int mid=(L+R)/2;
if(r<=mid){
return query(th*2,l,r,L,mid);
}else if(l>mid){
return query(th*2+1,l,r,mid+1,R);
}else{
return min(query(th*2,l,mid,L,mid),query(th*2+1,mid+1,r,mid+1,R));
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n-1;i++){
scanf("%lld",&sum[i]);
}
top11=top22=top1=top2=0;
sum[n]=0;
for(int i=n-1;i>=0;i--){
sum[i]+=sum[i+1];
}
memset(node1,-1,sizeof(node1));
memset(node2,-1,sizeof(node2));
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
u--;v--;
if(v>u)add_side1(u,v,(ll)w);
else if(v<u)add_side2(v,u,(ll)w);
}
int q;
scanf("%d",&q);
for(int i=0;i<q;i++){
int u,v;
scanf("%d%d",&u,&v);
u--;v--;
if(v>u){
q1[top1].set(u,v,i);
top1++;
}else{
q2[top2].set(u,v,i);
top2++;
}
}
sort(q1,q1+top1,cmp1);
sort(q2,q2+top2,cmp2);
build(1,0,n-1);
int u=n-1;
int t1=0,t2=0;
while(u>=0){
for(int i=node1[u];i!=-1;i=side1[i].next){
int v=side1[i].to;
insert(1,v,0,n-1,side1[i].w+sum[v]-sum[u]);
}
while(t1<top1&&q1[t1].u==u){
ans[q1[t1].id]=sum[u]-sum[q1[t1].v]+min(query(1,u,q1[t1].v,0,n-1),(ll)0);
t1++;
}
u--;
}
build(1,0,n-1);
int v=0;
while(v<=n-1){
for(int i=node2[v];i!=-1;i=side2[i].next){
int u=side2[i].to;
insert(1,u,0,n-1,side2[i].w+sum[v]-sum[u]);
}
while(t2<top2&&q2[t2].v==v){
if(q2[t2].u!=v)ans[q2[t2].id]=sum[q2[t2].u]-sum[v]+query(1,q2[t2].u,n-1,0,n-1);
else ans[q2[t2].id]=0;
t2++;
}
v++;
}
for(int i=0;i<q;i++){
printf("%lld\n",ans[i]);
}
}
}
//void insert(int th,int x,int L,int R,int v){