NOIP2017提高组D1T3[逛公园]

题意:

给定一个有向图,设1到n的最短路径长度为d,问有多少从1到n长度在d~d+k(k给定)之间的路径(mod p)。

题解:

先两遍Dijkstra或SPFA算出最短路径树,然后在最短路径树上拓扑排序,若不能排序(意味着有0环)则有无数条满足要求的路径,否则排完序后按拓扑序DP得到答案。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int MAXN = 100005;
const int MAXM = 200005;
const int INF = 1000000001;

struct Edge
{
	int to;
	int len;
	int pre;
	int next;
}edge[2][MAXM];

int T,n,m,K,p,id[2];
int head,tail;
int first[2][MAXN];
int dist[2][MAXN];
int seq[MAXN];
int degree[MAXN];
int f[MAXN][55];

bool mark[MAXN];

stack S;

void addE(bool dir,int u,int v,int c)
{
	edge[dir][edge[dir][first[dir][u]].pre = ++id[dir]] = (Edge){v,c,0,first[dir][u]};
	first[dir][u] = id[dir];
}

int u[MAXM],v[MAXM],c[MAXM];

void SPFA(bool dir)
{
	queue > Q;
	for (int i = 1;i <= n;i++)
		dist[dir][i] = INF;
	dist[dir][1 + dir * (n - 1)] = 0;
	Q.push(make_pair(0,1 + dir * (n - 1)));
	while (!Q.empty())
	{
		int now = Q.front().second,dis = Q.front().first;
		Q.pop();
		if (dis > dist[dir][now])
			continue;
		for (int i = first[dir][now];i;i = edge[dir][i].next)
			if (dist[dir][edge[dir][i].to] > dis + edge[dir][i].len)
			{
				dist[dir][edge[dir][i].to] = dis + edge[dir][i].len;
				Q.push(make_pair(dist[dir][edge[dir][i].to],edge[dir][i].to));
			}
	}
}

bool topo_sort()
{
	for (int i = 1;i <= m;i++)
		if (mark[u[i]] && mark[v[i]] && dist[0][u[i]] + c[i] == dist[0][v[i]])
			degree[v[i]]++;
	head = 1,tail = 0;
	for (int i = 1;i <= n;i++)
		if (mark[i] && !degree[i])
			seq[++tail] = i;
	while (head <= tail)
	{
		int now = seq[head++];
		for (int i = first[0][now];i;i = edge[0][i].next)
			if (mark[edge[0][i].to] && dist[0][now] + edge[0][i].len == dist[0][edge[0][i].to])
			{
				degree[edge[0][i].to]--;
				if (!degree[edge[0][i].to])
					seq[++tail] = edge[0][i].to;
			}
	}
	int cnt = 0;
	for (int i = 1;i <= n;i++)
		cnt += mark[i];
	return cnt == tail;
}

inline char gc()
{
	static char buf[131072],*p1 = buf,*p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,131072,stdin),p1 == p2) ? EOF : *p1++;
}

inline int read()
{
	char ch = gc();
	int sum = 0;
	while (!(ch >= '0' && ch <= '9'))
		ch = gc();
	while (ch >= '0' && ch <= '9')
		sum = sum * 10 + ch - '0',ch = gc();
	return sum;
}

int main()
{
	freopen("park.in","r",stdin);
	freopen("park.out","w",stdout);
	ios::sync_with_stdio(false);
	T = read();
	while (T--)
	{
		memset(first,0,sizeof(first));
		memset(degree,0,sizeof(degree));
		memset(f,0,sizeof(f));
		memset(mark,0,sizeof(mark));
		id[0] = id[1] = 0;
		n = read(),m = read(),K = read(),p = read();
		for (int i = 1;i <= m;i++)
			u[i] = read(),v[i] = read(),c[i] = read(),addE(0,u[i],v[i],c[i]),addE(1,v[i],u[i],c[i]);
		SPFA(0);
		SPFA(1);
		id[0] = 0;
		for (int i = 1;i <= n;i++)
			if (dist[0][i] + dist[1][i] <= dist[0][n] + K)
				mark[i] = true;
		if (!topo_sort())
		{
			cout << -1 << endl;
			continue;
		}
		f[1][0] = 1;
		int ans = 0;
		Edge *t = edge[0];
		for (int k = 0;k <= K;k++)
		{
			for (int i = 1;i <= n;i++)
				if (dist[0][i] + dist[1][i] + k > dist[0][n] + K)
					mark[i] = false;
			for (int i = 1;i <= tail;i++)
				if (mark[seq[i]])
				{
					int now = seq[i],l,to,add = f[now][k];
					if (!add)
						continue;
					for (int j = first[0][now];j;j = t[j].next)
						if (mark[to = t[j].to])
						{
							l = dist[0][now] + k + t[j].len - dist[0][to = t[j].to];
							if (l <= K)
								(f[to][l] += add) %= p;
							else
							{
								if (j == first[0][now])
									first[0][now] = t[j].next;
								else if (t[t[j].pre].next = t[j].next)
									t[t[j].next].pre = t[j].pre;
							}
						}
						else
						{
							if (j == first[0][now])
								first[0][now] = t[j].next;
							else if (t[t[j].pre].next = t[j].next)
								t[t[j].next].pre = t[j].pre;
						}
				}
		}
		for (int k = 0;k <= K;k++)
		{
			ans += f[n][k];
			if (ans >= p)
				ans -= p;
		}
		cout << ans << endl;
	}
	return 0;
}


你可能感兴趣的:(NOIP)