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;
}