• [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();
    	}
    }
    
  • 相关阅读:
    用属性封装 Session 及 VIewState 的存取
    正则表达式的一些重要概念
    通用权限的思路。只是一个简单的思路。
    IBATISNETNET 1.3 开发指南系列文章
    Serializable===net对象序列化
    使用Asp.Net构建安全网站
    用汽车售票系统谈数据库结构设计
    图文描述Vs2005制作WEB应用程序安装包的方法[E8.Net正式用户可以找我们获取全部代码参考]
    《基于.NET平台的分层架构实战》系列文章索引
    javascript中outerHTML innerHTML innerTEXT 三者的区别
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12096023.html
Copyright © 2020-2023  润新知