[SDOI2017]天才黑客

天才黑客

題解

貌似挺簡單的一道dijkstra

其實到最後發現就是建圖跑一次dijk,就是建圖有些噁心而已。

我們發現這道題本質上就是一個找最短路,關於每條邊的距離它給了一個trie樹來形容。

由於每條邊的長度會受到它當前所帶的字符串的影響,所以我們就順理成章的想到了拆點。

明顯不能把一個點拆成它所有的入邊個點,看了看數據範圍,空間炸了。

於是我們考慮另一種建圖方式,來拆邊:

我們可以把一個邊拆成2個點,分別爲它的入點與出點,它們之間連代表這條邊邊權的邊。對於兩條不同的邊,若他們可以到達,及他們爲(a,b)與(b,c),那麼我們就在它們直接連一條爲它們lcp的邊。最後從超源向點1連接了的邊連邊即可。統計答案時就統計所有能到這個點的邊的最小值即可。

不過這種圖還是會被菊花圖給炸掉。O\left(m^{2} \right )條邊,不炸纔怪。

我們貌似還得想辦法優化。由於這是一個trie樹上的字符串,當m> k時一定會出現許多重複邊權的邊,因爲最多隻會O\left(n+k \right )種邊權。

好像還可以在trie樹上下功夫。於是我們按dfs序將每個邊排個序,像後綴數組一樣,它們中相鄰兩個點的lcp長度就是他們的height數組。那麼lcp就成了區間的最小值。這個最小值通過單調棧與線段樹很簡單就可以求出來了。

可貌似這種O\left(mlog^{2}{m} \right )的方法還可以通過前綴和與後綴和來優化。

我們還可以不用單調棧來處理這個height數組。

因爲對於任何一個i某側的入點都可以以不超過h_{i}的代價來的i另一側的出點,所以我們就可以讓兩邊的出入點連一條邊權爲h_{i}的邊,很明顯這樣就可以用極少的邊完成連邊的工作了。

但這樣就不支持同時處理從左向右連與從右向左連。

我們可以把一條邊拆成4個點,分別處理兩種連邊。然後就可以建圖跑一邊dijk就溜了

源碼

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define MAXN 20005
typedef long long LL; 
#define int LL 
#define gc() getchar()
const int INF=0x7f7f7f7f;
template<typename _T>
inline void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
int t,n,m,k,dfn[MAXN],dep[MAXN];
namespace Tree{
	const int N=2e4+5;
	struct ming{
		int v,w;
		ming(){}
		ming(int V,int W){v=V;w=W;}
		friend bool operator < (ming x,ming y){return x.w<y.w;}
	};
	int f[N][20],cnt;vector<ming> trie[N];
	void addEdge(int u,int v,int w){
		trie[u].push_back(ming(v,w));
	}
	void dfs(int u){
		for(int i=1;i<19;i++)f[u][i]=f[f[u][i-1]][i-1];
		dfn[u]=++cnt;vector<ming>::iterator it;
		for(it=trie[u].begin();it!=trie[u].end();++it)
			dep[it->v]=dep[u]+1,f[it->v][0]=u,dfs(it->v);
	}
	void init(){
		for(int i=1;i<=k;i++)sort(trie[i].begin(),trie[i].end());
		dfs(1);
	}
	int lca(int a,int b){
		if(dep[a]>dep[b])swap(a,b);
		for(int i=18;i>=0;i--)if(dep[a]<=dep[f[b][i]])b=f[b][i];
		if(a==b)return a;
		for(int i=18;i>=0;i--)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
		return f[a][0];
	}
	void clear(){
		for(int i=1;i<=k;i++)trie[i].clear();
		memset(f,0,sizeof(f));cnt=0;
	}
}
namespace Graph{
	const int N=5e4+5,V=N<<2,E=20*N;
	int tp[V],tott;
	vector<int> eif[N],eof[N],eib[N],eob[N],tr;
	int to[E],head[V],tot,nxt[E],paid[E],s;
	bool cmp(int a,int b){return dfn[tp[a]]<dfn[tp[b]];}
	void addEdge(int u,int v,int w){
		to[++tot]=v;paid[tot]=w;
		nxt[tot]=head[u];head[u]=tot;
	}
	void insert(int u,int v,int w,int d){
		if(u==1)addEdge(s,tott+1,0),addEdge(s,tott+3,0);
		for(int i=1;i<=4;i++)tp[tott+i]=d;
		addEdge(tott+1,tott+2,w);addEdge(tott+1,tott+4,w);
		addEdge(tott+3,tott+4,w);addEdge(tott+3,tott+2,w);
		eof[u].push_back(++tott);eif[v].push_back(++tott);
		eob[u].push_back(++tott);eib[v].push_back(++tott);
	}
	void build(){
		for(int u=1;u<=n;u++){
			sort(eof[u].begin(),eof[u].end(),cmp);
			sort(eif[u].begin(),eif[u].end(),cmp);
			sort(eob[u].begin(),eob[u].end(),cmp);
			sort(eib[u].begin(),eib[u].end(),cmp);
			int siz1=eof[u].size(),siz2=eif[u].size();
			int siz3=eob[u].size(),siz4=eib[u].size();
			for(int i=0;i<siz1-1;i++)addEdge(eof[u][i],eof[u][i+1],0);
			for(int i=0;i<siz2-1;i++)addEdge(eif[u][i],eif[u][i+1],0);
			for(int i=0;i<siz3-1;i++)addEdge(eob[u][i+1],eob[u][i],0);
			for(int i=0;i<siz4-1;i++)addEdge(eib[u][i+1],eib[u][i],0);
			tr.resize(eof[u].size()+eif[u].size());
			merge(eif[u].begin(),eif[u].end(),eof[u].begin(),eof[u].end(),tr.begin(),cmp);
			for(int t=0,i=0,j=0;t<tr.size()-1;t++){
				(tr[t]&1)?j++:i++;int w=dep[Tree::lca(tp[tr[t]],tp[tr[t+1]])];
				if(i!=0&&j!=eof[u].size())addEdge(eif[u][i-1],eof[u][j],w);
				if(i!=eib[u].size()&&j!=0)addEdge(eib[u][i],eob[u][j-1],w);
			}
		}
	}
	struct ming{
		int u,d;
		ming(){}
		ming(int U,int D){u=U;d=D;}
		friend bool operator < (ming a,ming b){return a.d>b.d;}
	};
	int dis[V];bool vis[V];
	priority_queue<ming> q;
	void solve(){
		for(int i=1;i<=tott;i++)dis[i]=(1LL<<40);dis[s]=0;
		q.push(ming(s,0));
		while(!q.empty()){
			ming now=q.top();q.pop();
			if(vis[now.u])continue;vis[now.u]=1;
			for(int i=head[now.u];i;i=nxt[i]){
				int v=to[i];if(vis[v])continue;
				if(dis[v]>now.d+paid[i])
					dis[v]=now.d+paid[i],q.push(ming(v,dis[v]));
			}
		}
		for(int i=2;i<=n;i++){
			int res=(1LL<<40),siz1=eif[i].size(),siz2=eib[i].size();
			for(int j=0;j<siz1;j++)res=min(res,dis[eif[i][j]]);
			for(int j=0;j<siz2;j++)res=min(res,dis[eib[i][j]]);
			printf("%lld\n",res);
		}
	} 
	void clear(){
		for(int i=1;i<=n;i++){
			eif[i].clear();eof[i].clear();
			eib[i].clear();eob[i].clear();
		}
		for(int i=1;i<=tott+1;i++)head[i]=0,vis[i]=false;
		tot=tott=0;
	}
}
signed main(){
	read(t);
	while(t--){
		read(n);read(m);read(k);Graph::s=4*m+1;
		for(int i=1;i<=m;i++){
			int u,v,w,d;
			read(u);read(v);read(w);read(d);
			Graph::insert(u,v,w,d);
		}
		for(int i=1;i<k;i++){
			int u,v,w;
			read(u);read(v);read(w);
			Tree::addEdge(u,v,w);
		}		
		Tree::init();Graph::build();Graph::solve();
		Tree::clear();Graph::clear();
	}
    return 0;
}

謝謝!!!

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