zoj 3724 離線+線段樹

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章