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){