zoukankan      html  css  js  c++  java
  • NOIP2017提高组Day1T3 逛公园 洛谷P3953 Tarjan 强连通缩点 SPFA 动态规划 最短路 拓扑序

    原文链接https://www.cnblogs.com/zhouzhendong/p/9258043.html

    题目传送门 - 洛谷P3953

    题目传送门 - Vijos P2030

    题意

      给定一个有向图,有 $n$ 个节点 $m$ 条边,边权值 $in[0,1000]$ 。

      小明要从 $1$ 走到 $n$ ,要求路径长度最大为 $d+k$ ,其中 $d$ 为 $1$ 到 $n$ 最短路长度。

      问小明有多少种走法,答案对 $p$ 取模。如果有无数种走法,那么输出 $-1$ 。

      $nleq 100000,mleq 200000,0leq kleq 50,1leq p leq 10^9$

    题解

      我们首先看看没有 $0$ 边,即答案不为 $-1$ 的情况。

      显然我们可以先 SPFA 跑一遍最短路。

      我们记 $dis_i$ 表示到节点 $i$ 的最短路长度。

      令 $dp_{i,j}$ 表示到达第 $i$ 个节点,路径长度为 $dis_i+j$ 的路径个数。

      显然可以在最短路网上生成的最短路 DAG 上搞一个拓扑序,然后 $Theta(nk)$ DP 解决。

      那么如果出现了 $0$ 环呢?

      首先我们不能草率的定下“图中有 $0$ 环答案就是 $-1$”这种结论。

      当然,“ $1$ 至 $n$ 通路上有零环答案是 $-1$” 也是错的。

      考虑到 $0$ 环上面可以随意转移,所以同一个 $0$ 环上的节点就像一个节点一样。

      所以我们可以 Tarjan 缩点一波。具体地:如果边权为 $0$ ,那么视为有边,否则视为无边。强连通缩点建立新图。

      于是我们在新环上面做 DP 。

      考虑在 DP 的过程中,一旦遇到零环上的点 $i$ ,一旦 $dp_{i,j}>0$ ,那么 $dp_{i,j}=infty$ 。

      并用此更新后面的点。

      这里需要注意一个细节:由于我们计算的 $dp_{i,j}$ 是模意义下的,当 $dp_{i,j}equiv 0pmod p$ 时,不一定满足 $dp_{i,j}=0$,所以我们需要再开一个数组来记录 $dp_{i,j}$ 的特性:是 $0$ 、有限正整数 还是 $infty$ 。

      然后我 TLE 了……蒟蒻自带大常数啊 QAQ ,只能卡常了。

      搞了好久之后,才想到好的做法。由于数组模拟链表跳着访问数组会很慢的,所以:

        可以通过把要访问的边按照顺序放在连续的存储单元内,并顺序访问来提高速度。

      结果卡了两倍多的常数,过掉了。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005,M=N*2,K=55;
    int T,n,m,k,p;
    int read(){
    	int x=0;
    	char ch=getchar();
    	while (!('0'<=ch&&ch<='9'))
    		ch=getchar();
    	while ('0'<=ch&&ch<='9')
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    struct Gragh{
    	int cnt,x[M],y[M],z[M],nxt[M],fst[N];
    	void clear(){
    		cnt=0;
    		memset(fst,0,sizeof fst);
    	}
    	void add(int a,int b,int c){
    		y[++cnt]=b,x[cnt]=a,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt;
    	}
    }g,g2;
    int Time,top,tot,bh[N],dfn[N],low[N],vis[N],st[N],inst[N],Nodecnt[N];
    int dp[N][K],type[N][K];
    int X[M],Y[M],Z[M],e;
    void Tarjan(int x){
    	vis[x]=1,dfn[x]=low[x]=++Time;
    	st[++top]=x,inst[x]=1;
    	for (int i=g.fst[x];i;i=g.nxt[i]){
    		if (g.z[i]>0)
    			continue;
    		int y=g.y[i];
    		if (!vis[y]){
    			Tarjan(y);
    			low[x]=min(low[x],low[y]);
    		}
    		else if (inst[y])
    			low[x]=min(low[x],low[y]);
    	}
    	if (dfn[x]==low[x]){
    		tot++;
    		bh[st[top]]=tot;
    		inst[st[top]]=0;
    		while (st[top--]!=x){
    			bh[st[top]]=tot;
    			inst[st[top]]=0;
    		}
    	}
    }
    int q[N],head,tail,qmod,dis[N],f[N],in[N];
    void SPFA(int S){
    	memset(f,0,sizeof f);
    	for (int i=0;i<N;i++)
    		dis[i]=1.5e9;
    	head=tail=0,qmod=tot+2;
    	dis[S]=0,f[S]=1,q[++tail]=S;
    	while (head!=tail){
    		int x=q[head=(head+1)%qmod],y;
    		f[x]=0;
    		for (int i=g2.fst[x];i;i=g2.nxt[i])
    			if (dis[y=g2.y[i]]>dis[x]+g2.z[i]){
    				dis[y]=dis[x]+g2.z[i];
    				if (!f[y]){
    					f[y]=1;
    					q[tail=(tail+1)%qmod]=y;
    				}
    			}
    	}
    }
    void solve(){
    	n=read(),m=read(),k=read(),p=read();
    	int S=1,T=n;
    	g.clear();
    	for (int i=1;i<=m;i++){
    		int a=read(),b=read(),c=read();
    		g.add(a,b,c);
    	}
    	Time=top=tot=0;
    	memset(bh,0,sizeof bh);
    	memset(st,0,sizeof st);
    	memset(dfn,0,sizeof dfn);
    	memset(low,0,sizeof low);
    	memset(vis,0,sizeof vis);
    	memset(inst,0,sizeof inst);
    	for (int i=1;i<=n;i++)
    		if (!vis[i])
    			Tarjan(i);
    	g2.clear();
    	for (int i=1;i<=g.cnt;i++)
    		if (bh[g.x[i]]!=bh[g.y[i]])
    			g2.add(bh[g.x[i]],bh[g.y[i]],g.z[i]);
    	memset(Nodecnt,0,sizeof Nodecnt);
    	for (int i=1;i<=n;i++)
    		Nodecnt[bh[i]]++;
    	S=bh[S],T=bh[T];
    	SPFA(S);
    	memset(in,0,sizeof in);
    	for (int i=1;i<=g2.cnt;i++)
    		if (dis[g2.x[i]]+g2.z[i]==dis[g2.y[i]])
    			in[g2.y[i]]++;
    	head=tail=0;
    	for (int i=1;i<=tot;i++)
    		if (in[i]==0)
    			q[++tail]=i;
    	while (head<tail){
    		int x=q[++head],y;
    		for (int i=g2.fst[x];i;i=g2.nxt[i])
    			if (dis[x]+g2.z[i]==dis[g2.y[i]]){
    				in[g2.y[i]]--;
    				if (in[g2.y[i]]==0)
    					q[++tail]=g2.y[i];
    			}
    	}
    	memset(type,0,sizeof type);
    	memset(dp,0,sizeof dp);
    	dp[S][0]=type[S][0]=1;
    	int ans=0;
    	e=0;
    	for (int iq=1;iq<=tot;iq++)
    		for (int i=g2.fst[q[iq]];i;i=g2.nxt[i]){
    			e++;
    			X[e]=g2.x[i];
    			Y[e]=g2.y[i];
    			Z[e]=g2.z[i];
    		}
    	for (int ix=0;ix<=k;ix++){
    		int x,y,z;
    		for (int i=1;i<=tot;i++)
    			if (Nodecnt[i]>1&&type[i][ix]==1)
    				type[i][ix]=2;
    		for (int i=1;i<=e;i++){
    			x=X[i],y=Y[i],z=Z[i];
    			if (type[x][ix]==0)
    				continue;
    			int iy=dis[x]+ix+z-dis[y];
    			if (iy>k||type[y][iy]==2)
    				continue;
    			type[y][iy]=type[x][ix];
    			if (type[x][ix]==1){
    				dp[y][iy]=dp[y][iy]+dp[x][ix];
    				if (dp[y][iy]>=p)
    					dp[y][iy]-=p;
    			}
    		}
    		if (type[T][ix]==2){
    			ans=-1;
    			break;
    		}
    		else if (type[T][ix]==1)
    			ans=(ans+dp[T][ix])%p;
    	}
    	printf("%d
    ",ans);
    }
    int main(){
    	T=read();
    	while (T--)
    		solve();
    	return 0;
    }
    

      

  • 相关阅读:
    网站结构之扁平结构与树形结构的区分
    如何提高网站的访问速度
    CSS透明度大汇总
    Microsoft.AlphaImageLoader滤镜讲解
    浏览器的渲染原理简介
    ACM思维题训练 Section A
    CF--思维练习--CodeForces
    CF--思维练习--CodeForces
    CF--思维练习--CodeForces
    CF思维联系--CodeForces
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/9258043.html
Copyright © 2011-2022 走看看