zoukankan      html  css  js  c++  java
  • [SDOI2017]天才黑客 (虚树+最短路)

    [SDOI2017]天才黑客 (虚树+最短路)

    (原谅我写不出简单题意)

    可以看到与\(trie\)树上的字母以及\(lcp\)并没有关系。。

    以边作为点,可以写出一个非常简单的最短路\(dis_i=min \lbrace dis_j+dep_{LCA(d_i,d_j)}+c_i|v_j=u_i\rbrace\),还可以建立\(u_0=v_0=1,d_0=1,c_0=0\)作为原点

    但是恶意卡的话有\(m^2\)条边...

    所以要考虑优化连边

    对于每个点\(x\),把所有\(u_i=x\)\(v_i=x\)\(d_i\)提出来,建出一棵虚树,然后我们在虚树上连边

    对于虚树上的点,我们建立正点和反点,正点向虚树上的父亲连边,反点向儿子连边,边权均为\(0\)

    所有\(v_i=x\)的边向虚树上\(d_i\)的正点连一条为\(0\)的边

    所有\(u_i=x\)的边,虚树上\(d_i\)的反点连过来一条为\(c_i\)的边

    考虑\(LCA\)的三种情况,在走到\(LCA\)时将\(dep_{LCA(d_i,d_j)}\)考虑进来

    1.对于\(LCA(d_i,d_j)=d_i\)的情况,直接让\(i\)\(d_i\)的反点连一条权为\(dep_{d_i}\)的边,在反点间转移即可

    2.对于\(LCA(d_i,d_j)=d_j\)的情况,同理的,让\(d_j\)的正点向\(j\)连一条权为\(dep_{d_j}\)的边即可

    3.最后一种情况比较复杂,因为我们要从\(LCA\)走过去,考虑枚举\(LCA\),让转移直接跳过\(LCA\),在\(LCA\)的儿子之间转移。这样能够保证转移无误,但是如果直接连边依然会被菊花图卡到\(n^2\),如果我们建立一个虚点连边的话,会存在自己向自己转移的非法情况。所以我们考虑前缀后缀连边,把所有儿子打成一个序列\(son_i\),然后建立两个虚点列\(a_i,b_i\),然后让\(a_i\rightarrow a_{i-1},b_i \rightarrow b_{i+1}\)\(son_i \rightarrow a_{i-1},son_i \rightarrow b_{i+1}\),把其中\(son_i\)出发的边边权设为\(dep_{LCA}\)即可

    注意类似的连边方式都要避免产生负边,否则不能使用\(Dijkstra\),会因为\(SPFA\)而被卡掉..

    #include<bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define reg register
    typedef long long ll;
    #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
    char IO;
    int rd(){
    	int s=0;
    	while(!isdigit(IO=getchar()));
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return s;
    }
    
    const int N=1e6+10,M=N/20+4;
    const ll INF=1e18;
    
    bool be;
    int n,m,k;
    struct Edge{
    	int from,to,pos,w,id;
    }e[M];
    vector <Edge> S[M],T[M];
    void AddEdge(int u,int v,int c,int d,int id) {
    	S[u].pb(e[id]=(Edge){u,v,d,c,id});
    	T[v].pb(e[id]);
    }
    
    int fa[M],dep[M],sz[M],top[M],son[M],L[M],R[M],dfn;
    vector <int> G[M];
    typedef pair<int,int> Pii;
    vector <Pii> E[N];
    
    void dfs(int u,int f) {
    	fa[u]=f,L[u]=++dfn,sz[u]=1,son[u]=0;
    	for(int v:G[u]) {
    		dep[v]=dep[u]+1;
    		dfs(v,u);
    		sz[u]+=sz[v];
    		if(sz[son[u]]<sz[v]) son[u]=v;
    	}
    	R[u]=dfn;
    }
    void dfs2(int u,int t) {
    	top[u]=t;
    	if(son[u]) dfs2(son[u],t);
    	for(int v:G[u]) if(v!=son[u]) dfs2(v,v);
    }
    int LCA(int x,int y) {
    	while(top[x]!=top[y]) {
    		if(dep[top[x]]<dep[top[y]]) y=fa[top[y]];
    		else x=fa[top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    
    int line[M],le[M],lc,cnt;
    
    int id[M],to[N],stk[M],Top;
    void Insert(int x){
    	int lca=LCA(stk[Top],x);
    	if(stk[Top]==lca) {
    		stk[++Top]=x,id[x]=++cnt;to[cnt]=x;
    		cnt++;to[cnt]=x;
    		return;
    	}
    	while(Top>1 && dep[stk[Top-1]]>=dep[lca]) {
    		E[id[stk[Top]]].pb((Pii){id[stk[Top-1]],0});
    		E[id[stk[Top-1]]+1].pb((Pii){id[stk[Top]]+1,0});
    		Top--;
    	}
    	if(stk[Top]==lca) {
    		stk[++Top]=x,id[x]=++cnt;to[cnt]=to[cnt+1]=x;
    		cnt++;
    		return;
    	}
    	if(dep[stk[Top]]>dep[lca]) {
    		id[lca]=++cnt;to[cnt]=to[cnt+1]=lca;
    		cnt++;
    
    		E[id[stk[Top]]].pb((Pii){id[lca],0});
    		E[id[lca]+1].pb((Pii){id[stk[Top]]+1,0});
    		Top--;
    		stk[++Top]=lca; 
    		stk[++Top]=x;
    		id[x]=++cnt;to[cnt]=to[cnt+1]=x;
    		cnt++;
    	}
    }
    
    
    void PreMake() {
    	rep(u,1,n) {
    		if(!T[u].size() || !S[u].size()) continue;
    		lc=0;
    		rep(i,0,T[u].size()-1) line[++lc]=T[u][i].pos;
    		rep(i,0,S[u].size()-1) line[++lc]=S[u][i].pos;
    		sort(line+1,line+lc+1,[&](int x,int y){return L[x]<L[y];});
    		lc=unique(line+1,line+lc+1)-line-1;
    		int tcnt=cnt;
    		stk[Top=1]=1,id[1]=++cnt;to[cnt]=to[cnt+1]=1;
    		cnt++;
    		rep(i,1,lc) if(line[i]!=1) Insert(line[i]); //建立虚树
    		while(Top>1) {
    			E[id[stk[Top]]].pb((Pii){id[stk[Top-1]],0});
    			E[id[stk[Top-1]]+1].pb((Pii){id[stk[Top]]+1,0});
    			Top--;
    		}
    		rep(i,0,T[u].size()-1) {  
    			E[T[u][i].id].pb((Pii){id[T[u][i].pos],0}); // 向正点连边
    			E[T[u][i].id].pb((Pii){id[T[u][i].pos]+1,dep[T[u][i].pos]}); //直接向反点连边,考虑dep
    		}
    		rep(i,0,S[u].size()-1) {
    			E[id[S[u][i].pos]+1].pb((Pii){S[u][i].id,S[u][i].w}); //反点连过来
    			E[id[S[u][i].pos]].pb((Pii){S[u][i].id,dep[S[u][i].pos]+S[u][i].w});//直接从正点连过来
    		}
    		rep(i,tcnt+1,cnt) if(~(i-tcnt)&1) {
    			lc=0;
    			rep(j,0,E[i].size()-1) 
                    if(E[i][j].first>tcnt && E[i][j].first<=iend) 
                        line[++lc]=E[i][j].first; //提出儿子的序列
    			if(lc<=1) continue;
    			int lst=-1; 
    			rep(j,1,lc) {
    				cnt++;
    				E[cnt].pb((Pii){line[j],0});
    				if(~lst) {
    					E[line[j]-1].pb((Pii){lst,dep[to[i]]});
    					E[cnt].pb((Pii){lst,0});
    				}
    				lst=cnt;//连接ai
    			}
    			lst=-1;
    			drep(j,lc,1) {
    				cnt++;
    				E[cnt].pb((Pii){line[j],0});
    				if(~lst) {
    					E[line[j]-1].pb((Pii){lst,dep[to[i]]});
    					E[cnt].pb((Pii){lst,0});
    				}
    				lst=cnt; //连接bi
    			}
    		}
    	}
    }
    
    struct Node{
    	int x;
    	ll d;
    	bool operator < (const Node __) const{
    		return d>__.d;
    	}
    };
    priority_queue <Node> que;
    ll dp[N],Ans[M];
    void Dijkstra(int st) {
    	rep(i,0,cnt) dp[i]=INF;
    	rep(i,1,n) Ans[i]=INF;
    	dp[0]=0;
    	que.push((Node){st,0});
    	while(!que.empty()) {
    		int u=que.top().x;
    		ll d=que.top().d;que.pop();
    		if(dp[u]<d) continue;
    		rep(i,0,E[u].size()-1) {
    			int v=E[u][i].first,w=E[u][i].second;
    			if(dp[v]>dp[u]+w) dp[v]=dp[u]+w,que.push((Node){v,dp[v]});
    		}
    	}
    	rep(i,1,m) Ans[e[i].to]=min(Ans[e[i].to],dp[i]);
    }
    
    bool ed;
    int main(){
    	rep(kase,1,rd()) {
    		n=rd(),m=rd(),k=rd();
    		AddEdge(1,1,0,1,0);
    		rep(i,1,m) {
    			int u=rd(),v=rd(),c=rd(),d=rd();
    			AddEdge(u,v,c,d,i);
    		}
    		rep(i,2,k) {
    			int u=rd(),v=rd(); rd();
    			G[u].pb(v);
    		}
    		dfn=0,dfs(1,0),dfs2(1,1);
    		cnt=m;
    		PreMake();
    		Dijkstra(0);
    		fprintf(stderr,"%d\n",cnt);
    		rep(i,2,n) printf("%lld\n",Ans[i]);
    		rep(i,0,cnt) E[i].clear();
    		rep(i,0,n) S[i].clear(),T[i].clear();
    		rep(i,0,k) G[i].clear();
    	}
    }
    
  • 相关阅读:
    trackr: An AngularJS app with a Java 8 backend – Part III
    trackr: An AngularJS app with a Java 8 backend – Part II
    21. Wireless tools (无线工具 5个)
    20. Web proxies (网页代理 4个)
    19. Rootkit detectors (隐形工具包检测器 5个)
    18. Fuzzers (模糊测试器 4个)
    16. Antimalware (反病毒 3个)
    17. Debuggers (调试器 5个)
    15. Password auditing (密码审核 12个)
    14. Encryption tools (加密工具 8个)
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12096023.html
Copyright © 2011-2022 走看看