因为A掉了d1t1,十分开心,把d1t3的代码调出来了。
一般情况下,noip每一天总有一道dp题,然而d1前两道题都不是,再看看第三题的数据范围,就能大概猜出是dp了。
这道题和最短路计数看上去很像。回想一下最短路计数的解法,大概是按照bfs序进行dp,dp[u]表示到节点u的条数。对于这道题而言,求的不是最短路条数而是长度不超过最短路+k的路径条数,那么就可以用dp[u][j]表示到节点u路径长度等于j的路径数。
至于如何转移,用dis[x]表示x到1的最短路长度,则当dis[u]+w[k]+j-dis[x]<=k有dp[x][dis[u]+w[k]+j-dis[vv]]+=dp[u][j]。
至于转移顺序,想到最短路计数是bfs序,而这道题有边权,那么就是dijkstra序(<-瞎编的词),其实就是dis[x]由小到大的顺序。
至于0边,需要建一个新图,图中只有0边。用一个拓扑排序找出0环,若环上存在点x满足1到x的距离+x到n的距离<=1到n的距离+k,则这个环会被计数,输出-1。还有一点需要注意的是,当出现0边时,dp顺序不单单是dis[x]的顺序。当出现x--0-->y,显然是要先算dp[x]的。这是就需要用到之前算出的拓扑序,当两个点的dis相等时,先算拓扑序靠前的那个。
下面是代码,我略作修改,使它有可能RE。

#include<iostream> #include<iomanip> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #define stnd struct node #define nd node #define oops operator #define maxn 400010 #define maxm 800010 #define ll long long using namespace std; ll v[maxm],w[maxm],fir[maxn],nxt[maxm],cnt;//For roads. ll fv[maxm],fw[maxm],ffir[maxn],fnxt[maxm],fcnt;//For froads. ll tl,in[maxn],dep[maxn],ord[maxn],inq[maxn];//For topo sorting. ll n,m,inf,t,dp[maxn][60],kkk,p;;//For counting. ll dis[maxn],fdis[maxn];//For dijkstra. bool fl,vis[maxn][60]; stnd { ll x,y; bool oops <(const nd &zz)const { return y>zz.y; } }; inline ll read() { ll xx=0,ff=1; char ch=getchar(); while(isdigit(ch)==0 && ch!='-')ch=getchar(); if(ch=='-')ff=-1,ch=getchar(); while(isdigit(ch))xx=xx*10+ch-'0',ch=getchar(); return xx*ff; } void addedge(ll u1,ll v1,ll w1) { v[cnt]=v1,w[cnt]=w1,nxt[cnt]=fir[u1],fir[u1]=cnt++; fv[fcnt]=u1,fw[fcnt]=w1,fnxt[fcnt]=ffir[v1],ffir[v1]=fcnt++; } void dj() { memset(dis,0x7f,sizeof(dis)); priority_queue<stnd>q; stnd tmp; tmp.x=1,tmp.y=0; dis[1]=0; q.push(tmp); while(!q.empty()) { ll u=q.top().x,du=q.top().y;q.pop(); if(dis[u]<du)continue; for(ll k=fir[u];k!=-1;k=nxt[k]) { ll vv=v[k]; if(dis[vv]>dis[u]+w[k]) { dis[vv]=dis[u]+w[k]; tmp.x=vv,tmp.y=dis[vv]; q.push(tmp); } } } } void fdj() { memset(fdis,0x7f,sizeof(fdis)); priority_queue<stnd>q; stnd tmp; tmp.x=n,tmp.y=0; fdis[n]=0; q.push(tmp); while(!q.empty()) { ll u=q.top().x,du=q.top().y;q.pop(); if(fdis[u]<du)continue; for(ll k=ffir[u];k!=-1;k=fnxt[k]) { ll vv=fv[k]; if(fdis[vv]>fdis[u]+fw[k]) { fdis[vv]=fdis[u]+fw[k]; tmp.x=vv,tmp.y=fdis[vv]; q.push(tmp); } } } } void topoo(ll xx) { inq[xx]=1; ord[tl++]=xx; for(ll k=fir[xx];k!=-1;k=nxt[k]) { if(w[k]!=0)continue; ll vv=v[k]; in[vv]--; if(in[vv]==0) topoo(vv); } return; } void topo() { tl=1; memset(inq,0,sizeof(inq)); memset(in,0,sizeof(in)); for(ll i=0;i<m;i++) if(w[i]==0)in[v[i]]++; for(ll i=1;i<=n;i++) if(inq[i]==0 && in[i]==0) topoo(i); for(ll i=1;i<=n;i++) { if(in[i]!=0 && dis[i]+fdis[i]<=dis[n]+kkk) fl=1; } } bool dfs(int u,int j) { if(vis[u][j])return 1; if(~dp[u][j])return 0; vis[u][j]=1; if(u==n)dp[u][j]=1; else dp[u][j]=0; for(int k=fir[u];k!=-1;k=nxt[k]) { int vv=v[k],tt=dis[u]-dis[vv]+w[k]+j; if(tt<=kkk && dis[u]+j+w[k]+fdis[vv]<=kkk+fdis[1]) { if(dfs(vv,tt))return 1; dp[u][j]+=dp[vv][tt]; dp[u][j]%=p; } } vis[u][j]=0; return 0; } int main() { t=read(); while(t--) { cnt=fcnt=fl=0; n=read(),m=read(),kkk=read(),p=read(); memset(fir,-1,sizeof(fir)); memset(ffir,-1,sizeof(ffir)); memset(dp,-1,sizeof(dp)); memset(vis,0,sizeof(vis)); for(ll i=1;i<=m;i++) { ll x=read(),y=read(),z=read(); addedge(x,y,z); } dj(); fdj(); topo(); if(fl)cout<<-1; else { dfs(1,0); cout<<dp[1][0]; } cout<<endl; } return 0; }