比赛 4043级2023省选模拟赛1 评测结果 AAAAAAAAAA
题目名称 逛公园 最终得分 100
用户昵称 shenben 运行时间 7.032 s
代码语言 C++ 内存使用 159.93 MiB
提交时间 2023-03-20 21:53:00
显示代码纯文本
#include<bits/stdc++.h>
#define reg register
using namespace std;
const int N=2e5+10;
struct edge{int from,to,weight;}E[N];
int n,m,k,p,head[N],nxt[N],cntedge;
inline int add_edge(int from,int to,int weight)
{//添加边,链式前向星存储图
	E[++cntedge]=(edge){from,to,weight};
	nxt[cntedge]=head[from];
	head[from]=cntedge;
	return 0;
}
int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
typedef pair<int,int> pairii;
priority_queue<pairii,vector<pairii>,greater<pairii> > Q;
int disS[N],disT[N];
bool vis[N];
int dijkstra(int *dis,int s)
{//优先队列优化的DJ算法
	for(int i=1;i<=n;i++) dis[i]=2e9,vis[i]=0;
	Q.push(pairii(dis[s]=0,s));
	while (!Q.empty())
	{
		int v=Q.top().second;Q.pop();
		if(vis[v]) continue;
		vis[v]=1;
		for(int i=head[v];i;i=nxt[i])
		if(dis[E[i].to]>dis[v]+E[i].weight)
		{//松弛操作
			int u=E[i].to;
			Q.push(pairii(dis[u]=dis[v]+E[i].weight,u));
		}
	}
	return 0;
}
int u[N],v[N],w[N],dfn[N],low[N],scc[N],sta[N],top,dfs_clock;
bool ins[N];
int tarjan(int x)
{//tarjan判环
	dfn[x]=low[x]=++dfs_clock;//时间戳
	ins[sta[++top]=x]=1;//进栈状态置1
	for(int i=head[x];i;i=nxt[i])
	{
		int v=E[i].to;
		if(!dfn[v])
			tarjan(v),low[x]=min(low[x],low[v]);
		else
			if(ins[v])
				low[x]=min(low[x],dfn[v]);
	}
	if(dfn[x]==low[x])
	{/*如果有强联通分量即环,那么将环上所有顶点的scc后继属性设置为环上DFN最小的结点的编号*/
		while(sta[top]!=x)
			scc[sta[top]]=x,ins[sta[top--]]=0;
		scc[x]=x;ins[x]=0;top--;
	}
	return 0;
}
inline int inc(int &x,int y)
{
	x+=y;
	if(x>=p)x%=p;
	return 0;
}
int degree[N][60],dp[N][60],q[N*60][2];
void outpt()
{
	for(reg int i=1;i<=n;i++)
		{
			for(reg int j=0;j<=k;j++)
				cout<<dp[i][j]<<' ';
			cout<<endl;
		}
		cout<<endl<<endl;
}
int work()
{
	scanf("%d%d%d%d",&n,&m,&k,&p);
	for(int i=1;i<=m;i++) u[i]=read(),v[i]=read(),w[i]=read();
	cntedge=0;
	for(int i=1;i<=n;i++) head[i]=0;
	for(int i=1;i<=m;i++) add_edge(v[i],u[i],w[i]);//反向边v-u
	/*将所有边反向,求N到其它结点的最短路*/
	dijkstra(disT,n);//起点为n到其它顶点
	//for(int i=1;i<=n;i++)cout<<disT[i]<<' ';cout<<endl;
	cntedge=0;
	for(int i=1;i<=n;i++) head[i]=0;
	for(int i=1;i<=m;i++) add_edge(u[i],v[i],w[i]);//正向边
	/*求1点到其它顶点的最短路*/
	dijkstra(disS,1);//起点为1到其它顶点
	
	cntedge=0;
	for(int i=1;i<=n;i++) head[i]=0;
	for(int i=1;i<=m;i++)
		if(!w[i])//构造所有0权边组成的图,寻找是否有0环
			add_edge(u[i],v[i],w[i]);
	dfs_clock=0;
	for(int i=1;i<=n;i++)dfn[i]=low[i]=scc[i]=0;//tarjan初始化,寻找0权环
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);/*在0权图上以尚未被访问的每个点为起点运行tarjan,寻找0环,
	如果存在scc属性设置为环上根结点的编号*/
	
	for(int i=1;i<=cntedge;i++)
	{//枚举0权图中的0权边
		int u=E[i].from,v=E[i].to;
		if(scc[u]==scc[v]&&disS[u]+disT[v]<=disS[n]+k)
		{/*如果存在0权环并且边u-v处在满足条件的路径上,即围绕0环会出现无穷多条满足条件的路径*/
			puts("-1");return 0;
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=0;j<=k;j++)//根据k将图分成k+1层,0~k
			dp[i][j]=degree[i][j]=0;
	//degree[i][j]表示第j层(0<=j<=k),1~i的路径中不超过disS[i]+j时i点的入度
	cntedge=0;
	for(int i=1;i<=n;i++) head[i]=0;
	for(int i=1;i<=m;i++) add_edge(u[i],v[i],w[i]);
	for(int i=1;i<=m;i++)
	{//枚举每一条边,计算每一层i的入度
		int x=u[i],y=v[i];
		for(int j=0;j<=k;j++)
		{//位置pos=起点1到x最短路值+w(x-y)+j-起点1到y的最短路值
			int pos=disS[x]+w[i]+j-disS[y];
			if(pos<=k)
				degree[y][pos]++;//入度加1
		}
	}
	
	int size=0;
	for(reg int j=0;j<=k;j++)for(reg int i=1;i<=n;i++)
		
			if(!degree[i][j])//入度为0的点入队
				++size,q[size][0]=i,q[size][1]=j;
	/*
	dp[v][x]表示1~v的路径中,比disS[v]多x的条数。
	dp[v][dis[u]+j+w(u,v)-dis[v]]=SUM(dp[u][j])  0<=j<=k   dis[u]+j+w-dis[v]<=k
	*/
	dp[1][0]=1%p;
	for(reg int i=1;i<=size;i++)
	{//按照拓扑排序的顺序转移状态
		reg int u=q[i][0],j=q[i][1];
		reg int cnt=dp[u][j];
		for(reg int i=head[u];i;i=nxt[i])
		{
			reg int v=E[i].to,pv=disS[u]+j+E[i].weight-disS[v];
			if(pv<=k)
			{
				inc(dp[v][pv],cnt);
				
				if(!--degree[v][pv])//入度减1后为0,入队等待拓扑
					++size,q[size][0]=v,q[size][1]=pv;
			}
		}
	}
	
	int ans=0;
	for(int i=0;i<=k;i++)
		inc(ans,dp[n][i]);//累加dp[n][i]
	printf("%d\n",ans);
	return 0;
}
int main()
{
	freopen("2017park.in","r",stdin);
	freopen("2017park.out","w",stdout);
	int T;
	scanf("%d",&T);
	while (T--)work();
	return 0;
}