P3953 逛公园

我太弱了看了万老爷的题解才完全明白,如果是考场上自己写的话估计只能跑 \(70pts\) 还是在指针完全用对的情况下,膜万老爷。

感觉这是一道提高和普及简单图论算法的大集合。

题目大意

\(1\)\(n\) 的所有路径中路径长度 \(\le\) 最短路长度 \(+k\) 的路径个数

题解

首先看到这道题,发现一个 \(\text{dp}\) 还是比较容易想到的,设 \(f_{i,j}\) 为到第 \(i\) 个点同时与 \(1\) 到该点最短路差为 \(j\) 的路径个数,状态转移易得为:

\[f_{i,j}\Rightarrow f_{v,j+dis[u]+val-dis[v]} \]

\[s.t.j+dis[u]+val-dis[v]\le k \]

然后发现边权有 \(0\) 的情况,需要判断是否会有无限种方案,相当于是问我们是否存在一条符合上述条件的路径中存在一个点它位于一个\(0\) 环中,这个东西可以拆分成两部分。

首先是判断一个点是否满足上述条件,这个可以通过跑正图和反图的最短路来判断。而是否位于一个 \(0\) 环上,可以通过建一个只有 \(0\) 边的最短路再通过跑正反图的拓扑排序来做。

而且,存在 \(0\) 边会使得我们的\(\text{dp}\) 顺序收到影响,此时利用 \(dis\) 和 拓扑序两个东西一起维护一下就可以了。

题目想明白还是很简单的,但是技巧性很足,好题。

额外部分

这道题作者在写代码的时候利用了一个叫做“结构体套结构体”的东西,很有意思,在这里分享一下。

结构体套结构体首先要注意的是你要以哪一个结构体作为你在最外层调用的。

比如我在写这道题的时候,是要把结构体 \(\text{Graph}\) 放在最外面的,所以我先声明它,但不定义。然后再声明和定义我需要内部放入的结构体,最后在定义 \(\text{Graph}\)

struct Graph;
struct short_path
{
	Graph *g;//在结构体嵌套时必须有一个结构体要为指针。
	Lint dis[2][N];
	priority_queue<Now> q;
	void dijkstra(int tag);
};
struct top_sort
{
	Graph *g;
	queue<int> q;
	int deg[N],ord[N],cnt;
	bool vis[2][N];
	void top(int tag); 
};
struct Graph
{
	struct Edge{int nxt,to,val;}e[2][M];int fir[2][N];
	void clear()
	{
		memset(fir,0,sizeof(fir));
		memset(e,0,sizeof(e));
	}
	void add(int u,int v,int w,int i,int tag)
	{
		e[tag][i]=(Edge){fir[tag][u],v,w};
		fir[tag][u]=i;
	}
	short_path short_p;
	top_sort top_s;
}g1,g2;

然后如果你要写结构体嵌套的函数的时候,如果是外层结构体的函数,可以直接放在定义中写。但是如果是内层结构体的函数同时需要调用外层的,如果放在定义中写,由于此时外层的结构体还未定义,所以是不能编译通过的。要将其放在全局中书写。

void short_path::dijkstra(int tag){

	for(int i=1;i<=n;++i) dis[tag][i]=1e18+7;
	if(!tag) dis[tag][1]=0,q.push((Now){0,1});
	else dis[tag][n]=0,q.push((Now){0,n});
	while(!q.empty())
	{
		Now tmp=q.top();q.pop();
		if(tmp.dis>dis[tag][tmp.id]) continue;
		for(int i=g->fir[tag][tmp.id];i;i=g->e[tag][i].nxt)
		{
			if(dis[tag][g->e[tag][i].to]>tmp.dis+g->e[tag][i].val)
			{
				dis[tag][g->e[tag][i].to]=tmp.dis+g->e[tag][i].val;
				q.push((Now){dis[tag][g->e[tag][i].to],g->e[tag][i].to});
			}
		}
	}
}
void top_sort::top(int tag)
{
	memset(deg,0,sizeof(deg)),cnt=0;
	memset(vis[tag],0,sizeof(vis[tag]));
	for(int i=1;i<=m;++i) deg[g->e[tag][i].to]++;
	for(int i=1;i<=n;++i) if(!deg[i]) q.push(i);
	while(!q.empty())
	{
		int tmp=q.front();q.pop();
		if(!tag) ord[tmp]=++cnt;
		vis[tag][tmp]=true;
		for(int i=g->fir[tag][tmp];i;i=g->e[tag][i].nxt)
		{
			deg[g->e[tag][i].to]--;
			if(!deg[g->e[tag][i].to])
			q.push(g->e[tag][i].to);
		}
	}
}

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define Lint long long
const int N=1e5+5,M=2e5+5,K=55;
int n,m,k;Lint MOD;
struct Now{Lint dis;int id;};
bool operator < (const Now a,const Now b){return a.dis>b.dis;}
struct Graph;
struct short_path
{
	Graph *g;//在结构体嵌套时必须有一个结构体要为指针。
	Lint dis[2][N];
	priority_queue<Now> q;
	void dijkstra(int tag);
};
struct top_sort
{
	Graph *g;
	queue<int> q;
	int deg[N],ord[N],cnt;
	bool vis[2][N];
	void top(int tag); 
};
struct Graph
{
	struct Edge{int nxt,to,val;}e[2][M];int fir[2][N];
	void clear()
	{
		memset(fir,0,sizeof(fir));
		memset(e,0,sizeof(e));
	}
	void add(int u,int v,int w,int i,int tag)
	{
		e[tag][i]=(Edge){fir[tag][u],v,w};
		fir[tag][u]=i;
	}
	short_path short_p;
	top_sort top_s;
}g1,g2;
void short_path::dijkstra(int tag){

	for(int i=1;i<=n;++i) dis[tag][i]=1e18+7;
	if(!tag) dis[tag][1]=0,q.push((Now){0,1});
	else dis[tag][n]=0,q.push((Now){0,n});
	while(!q.empty())
	{
		Now tmp=q.top();q.pop();
		if(tmp.dis>dis[tag][tmp.id]) continue;
		for(int i=g->fir[tag][tmp.id];i;i=g->e[tag][i].nxt)
		{
			if(dis[tag][g->e[tag][i].to]>tmp.dis+g->e[tag][i].val)
			{
				dis[tag][g->e[tag][i].to]=tmp.dis+g->e[tag][i].val;
				q.push((Now){dis[tag][g->e[tag][i].to],g->e[tag][i].to});
			}
		}
	}
}
void top_sort::top(int tag)
{
	memset(deg,0,sizeof(deg)),cnt=0;
	memset(vis[tag],0,sizeof(vis[tag]));
	for(int i=1;i<=m;++i) deg[g->e[tag][i].to]++;
	for(int i=1;i<=n;++i) if(!deg[i]) q.push(i);
	while(!q.empty())
	{
		int tmp=q.front();q.pop();
		if(!tag) ord[tmp]=++cnt;
		vis[tag][tmp]=true;
		for(int i=g->fir[tag][tmp];i;i=g->e[tag][i].nxt)
		{
			deg[g->e[tag][i].to]--;
			if(!deg[g->e[tag][i].to])
			q.push(g->e[tag][i].to);
		}
	}
}
bool tag[N];Lint dis_minn;
struct Point{Lint dis;int id,ord;}a[N];
bool cmp(Point a,Point b)
{
	if(a.dis!=b.dis) return a.dis<b.dis;
	return a.ord<b.ord;
}
Lint f[N][K],res;
void solve()
{
	cin>>n>>m>>k>>MOD;
	g1.clear(),g2.clear();
	memset(tag,0,sizeof(tag));
	for(int i=1,u,v,w;i<=m;++i)
	{
		scanf("%d%d%d",&u,&v,&w);
		g1.add(u,v,w,i,0);
		g1.add(v,u,w,i,1);
		if(w==0)
		{
			tag[u]=tag[v]=true;
			g2.add(u,v,w,i,0);
			g2.add(v,u,w,i,1);
		}
	}
	g1.short_p.g=&g1;
	g1.short_p.dijkstra(0),g1.short_p.dijkstra(1);
	g2.top_s.g=&g2;
	g2.top_s.top(0),g2.top_s.top(1);
	dis_minn=g1.short_p.dis[0][n];
	for(int i=1;i<=n;++i)
	{
		if(g1.short_p.dis[0][i]+g1.short_p.dis[1][i]<=dis_minn+k)
		{
			if(tag[i]&&!g2.top_s.vis[0][i]&&!g2.top_s.vis[1][i])
			{
				printf("-1\n");return ;
			}
		}
	}
	for(int i=1;i<=n;++i)
	{
		a[i].id=i;
		a[i].dis=g1.short_p.dis[0][i];
		a[i].ord=g2.top_s.ord[i];
	}
	sort(a+1,a+1+n,cmp);
	memset(f,0,sizeof(f));
	f[1][0]=1,res=0;
	for(int j=0;j<=k;++j)
	{
		for(int u=1;u<=n;++u)
		{
			for(int i=g1.fir[0][a[u].id];i;i=g1.e[0][i].nxt)
			{
				Lint tmp=j+a[u].dis+g1.e[0][i].val-g1.short_p.dis[0][g1.e[0][i].to];
				if(tmp<=k) f[g1.e[0][i].to][tmp]+=f[a[u].id][j],f[g1.e[0][i].to][tmp]%=MOD;
			}
		}
		res+=f[n][j],res%=MOD;
	}
	printf("%lld\n",res);
	return ;
}
int main()
{
	int T;cin>>T;
	while(T--) solve();
	return 0;
}
posted @ 2020-11-04 11:48  Point_King  阅读(90)  评论(0)    收藏  举报