Luogu P3953 [NOIP2017 提高组] 逛公园

题链

[P3953 NOIP2017 提高组] 逛公园 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

分析

首先,这是有向图,需要求长度一定的路径数,感觉像最短路径树

建出最短路径树,可以发现某些不能到达1或者不能到达n的点显然无用,所以全部删点

一条路径可以用若干树边(u,v,d[v]-d[u]+w[e]) (d[u]表示u->n的最短路径长度)唯一表示,且可以发现发现长度为1-n的最短路径长度+树边和

所以可以改造图,对每条边(u,v,w)(包括最短路径上的边)保留(u,v,d[v]-d[u]+w),然后分层图

如果d[v]-d[u]+w>0,对分层图没有影响

如果d[v]-d[u]+w=0,则会影响同一层

又发现:如果不能形成环,则DAG上DP即可,但如果形成环,如果可以经过则无解,如果不能经过则直接扔掉即可

考虑一下树边如果形成0环,相当于原图一直绕都长度也没变,所以原图中有0环,所以tarjan缩点判由于0环,再判能否经过(缩点后的点到1和n的距离和是否大于1和n的距离+K)

#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;

const int N=2e5+5,M=52;
int n,m,K,p,tot,dfn[N],low[N],st[N],top,col,co[N],sz[N],f[N][M];
int E1[N],E2[N],E3[N],d1[N],d[N],deg[N],Q[N];
struct A{int v,w; };
vector<A>V1[N],V[N];
vector<int>V2[N],V3;
bool fl[N];

void tar(int u) {
	dfn[u]=low[u]=++tot,st[++top]=u;
	for(int v:V2[u]) {
		if(!dfn[v]) tar(v);
			else if(!co[v]) low[u]=min(low[u],low[v]);
	}
	if(low[u]==dfn[u]) {
		co[u]=++col; sz[col]=1;
		for(;st[top]!=u;top--) {
			co[st[top]]=col,sz[col]++;
		}
		top--;
	}
}
inline bool operator >(A i,A j) {
	return i.w>j.w;
}
priority_queue<A,vector<A>,greater<A> >q;
void dij(vector<A>*V,int *d,int s) {
	for(int i=1;i<=n+n;i++) {
		d[i]=INF,fl[i]=0;
	}
	q.push((A){s,d[s]=0});
	while(!q.empty()) {
		while(!q.empty()&&fl[q.top().v]) q.pop();
		if(q.empty()) break;
		int u=q.top().v;fl[u]=1; q.pop();
		for(A v:V[u]) {
			if(d[v.v]>d[u]+v.w) {
				q.push((A){v.v,d[v.v]=d[u]+v.w});
			}
		}
	}
}

bool huan() {
	memset(dfn,0,sizeof(dfn));
	memset(co,0,sizeof(co));
	memset(sz,0,sizeof(sz));
	tot=top=col=0;
	for(int i=1;i<=n;i++) {
		if(!dfn[i]) tar(i);
	}
	for(int i=1;i<=m;i++) {
		int u=E1[i],v=E2[i],k=E3[i];
		if(sz[co[u]]>1) u=n+co[u];
		if(sz[co[v]]>1) v=n+co[v];
		if(u!=v) {
			V[u].push_back((A){v,k});
			V1[v].push_back((A){u,k});
		}
	}
	if(sz[co[1]]>1||sz[co[n]]>1) return 1;
	dij(V,d1,1),dij(V1,d,n);
	for(int i=1;i<=col;i++) {
		if(sz[i]>1&&d1[i+n]+d[i+n]<=d[1]+K) {
			return 1;
		}
	}
	return 0;
}
inline int mo(int x) {
	return x>=p?x-p:x;
}
int main() {
	int T; scanf("%d",&T);
	while(T--) {
		scanf("%d%d%d%d",&n,&m,&K,&p);
		for(int i=1;i<=n+n;i++) V[i].clear(),V1[i].clear(),V2[i].clear();
		for(int i=1;i<=m;i++) {
			scanf("%d%d%d",&E1[i],&E2[i],&E3[i]);
			if(E3[i]==0) {
				V2[E1[i]].push_back(E2[i]);
			}
		}
		if(huan()) {
			puts("-1");
			continue;
		}
		for(int i=1;i<=n+n;i++) {
			V[i].clear();
			V2[i].clear();
		}
		memset(deg,0,sizeof(deg));
		for(int i=1;i<=m;i++) {
			int u=E1[i],v=E2[i];
			if(d1[u]+d[u]<=d[1]+K&&d1[v]+d[v]<=d[1]+K){
				int t=d[v]+E3[i]-d[u];
				if(t<=K) {
					V[u].push_back((A){v,t});
				}
				if(t==0) {
					deg[v]++,V2[u].push_back(v);
				}
			}
		}
		int l=1,r=0; V3.clear();
		for(int i=1;i<=n;i++) {
			if(deg[i]==0) {
				Q[++r]=i;
				if(d1[i]+d[i]<=d[1]+K) {
					V3.push_back(i);
				}
			}
		}
		while(l<=r) {
			int u=Q[l]; l++;
			for(int v:V2[u]) {
				deg[v]--;
				if(deg[v]==0) {
					Q[++r]=v;
					V3.push_back(v);
				}
			}
		}
		memset(f,0,sizeof(f));
		f[1][0]=1; int ans=0;
		for(int j=0;j<=K;j++) {
			for(int u:V3) {
				if(f[u][j]){
					for(A v:V[u]) {
						if(j+v.w<=K) {
							f[v.v][j+v.w]=mo(f[v.v][j+v.w]+f[u][j]);
						}
					}
				}
			}
			ans=mo(ans+f[n][j]);
		}
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2021-04-28 14:26  wwwsfff  阅读(49)  评论(0编辑  收藏  举报