https://www.luogu.org/problemnew/show/3953
唉,我是真的菜;
这道题目傻的要死,自己思考的时候好像思维被限制住了一样的;
先看0边的影响,那么-1的情况就是很明显的;
当一条合法路径上的一个点是0环上的点,那么显然我们可以走这个0环无数次得到无数解;
那我们直接把0环找出来,然后对于环上的每一个点x
判断一下 通过1到n 通过x的最短路是否满足<=1到n的最短路+K
d1[x]+dn[x]<=d1[n]+K
如果可以的话直接出-1了;
那么我们接下来就可以直接把这些点去掉了;
对于每一个点,我们拆成51个点,0~50代表进过这个点x时所经过的路程与d1[x]的差;
不难发现这就是一个拓扑图;
5000000个点10000000条边的拓扑图;
然后我们显然可以拓扑排序统计方案,卡常卡出屎了;
看了题解发现有更简单的做法;
直接记忆化dfs
对于f[i][j]表示i点的第j个拆点的方案数;
直接枚举i点所有的边,然后dfs下一层;
由于我们枚举所有的f[n][k]k∈[0,50],那么我们要建反向图;
这样子跑的是真的快;
简单来说,为什么这个东西dfs跑得比非递归拓扑快;
原因其实很简单的;
因为我们跑dfs的时候从答案开始跑,跑出来的全是必要解;
然而拓扑排序跑满的;
#include
#define U(x,y) ((x-1)*(K+1)+y)
using namespace std;
inline int RR(){int v=0,k=1;char c=0;while('0'>c||c>'9'){if(c=='-')k=-1;c=getchar();}while('0'<=c&&c<='9')v=(v<<3)+(v<<1)+c-48,c=getchar();return v*k;}
inline void W(int x){if(x<0)putchar('-'),x=-x;if(x>9)W(x/10);putchar(x%10+48);}
inline void WW(int x){W(x);puts("");}
inline void read(int &x,int &y){x=RR();y=RR();}
const int N=1e5+5,KK=5e1+5,M=2e5+5;
struct cs{int to,nxt,v;}a1[M],an[M],a[M];
int h1[N],hn[N],h[N],l1,ln,ll,f[N][KK],d1[N],dn[N],A[N];
bool in[N],vi[N][KK];
int t,n,m,K,mo,x,y,z,ans;
void init1(int x,int y,int z){a1[++l1].to=y;a1[l1].v=z;a1[l1].nxt=h1[x];h1[x]=l1;}
void initn(int x,int y,int z){an[++ln].to=y;an[ln].v=z;an[ln].nxt=hn[x];hn[x]=ln;}
void init(int x,int y,int z){a[++ll].to=y;a[ll].v=z;a[ll].nxt=h[x];h[x]=ll;}
void spfa(cs a[],int head[],int d[],int S){
queue<int>Q;d[S]=0;Q.push(S);
while(!Q.empty()){
int x=Q.front();Q.pop();in[x]=0;
for(int k=head[x];k;k=a[k].nxt)
if(d[a[k].to]>d[x]+a[k].v){
d[a[k].to]=d[x]+a[k].v;
if(!in[a[k].to])in[a[k].to]=1,Q.push(a[k].to);
}
}
}
bool TP(cs a[],int head[]){
queue<int>Q;
for(int i=1;i<=n;i++)if(A[i]==0)Q.push(i);
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int k=head[x];k;k=a[k].nxt)
if(--A[a[k].to]==0)Q.push(a[k].to);
}
for(int i=1;i<=n;i++)if(A[i]&&d1[i]+dn[i]<=d1[n]+K)return 1;
return 0;
}
int dfs(int x,int y){
if(vi[x][y])return f[x][y];
vi[x][y]=1;f[x][y]=0;
if(!A[x])
if(!f[x][y])
for(int k=h[x];k;k=a[k].nxt)
if(0<=d1[x]+y-a[k].v-d1[a[k].to]&&d1[x]+y-a[k].v-d1[a[k].to]<=KK)
f[x][y]=(f[x][y]+dfs(a[k].to,d1[x]+y-a[k].v-d1[a[k].to]))%mo;
return f[x][y];
}
int main()
{
t=RR();
while(t--){
memset(h1,0,sizeof h1);
memset(hn,0,sizeof hn);
memset(h,0,sizeof h);
l1=ln=ll=0;
read(n,m);read(K,mo);
for(int i=1;i<=m;i++){
read(x,y);z=RR();
init1(x,y,z);
initn(y,x,z);
init(y,x,z);
}
memset(d1,2,sizeof d1);
memset(dn,2,sizeof dn);
spfa(a1,h1,d1,1);
spfa(an,hn,dn,n);
memset(hn,0,sizeof hn);
memset(A,0,sizeof A);
ln=0;
for(int i=1;i<=n;i++)
for(int k=h1[i];k;k=a1[k].nxt)
if(a1[k].v==0)initn(i,a1[k].to,0),A[a1[k].to]++;
if(TP(an,hn)){puts("-1");continue;}
memset(vi,0,sizeof vi);
vi[1][0]=1;f[1][0]=1;ans=0;
for(int i=0;i<=K;i++)ans=(ans+dfs(n,i))%mo;
WW(ans);
}
}
另外出题人原来要卡spfa的,结果验题人良心发现(准确来说是验题人让出题人良心发现);