洛谷P3953 逛公園【記憶化搜尋】
阿新 • • 發佈:2018-12-09
題目描述
策策同學特別喜歡逛公園。公園可以看成一張個點條邊構成的有向圖,且沒有 自環和重邊。其中1號點是公園的入口,號點是公園的出口,每條邊有一個非負權值, 代表策策經過這條邊所要花的時間。
策策每天都會去逛公園,他總是從1號點進去,從號點出來。
策策喜歡新鮮的事物,它不希望有兩天逛公園的路線完全一樣,同時策策還是一個 特別熱愛學習的好孩子,它不希望每天在逛公園這件事上花費太多的時間。如果1號點 到號點的最短路長為,那麼策策只會喜歡長度不超過的路線。
策策同學想知道總共有多少條滿足條件的路線,你能幫幫它嗎?
為避免輸出過大,答案對取模。
如果有無窮多條合法的路線,請輸出 。
輸入格式:
第一行包含一個整數 代表資料組數。
接下來組資料,對於每組資料: 第一行包含四個整數 每兩個整數之間用一個空格隔開。
接下來行,每行三個整數,代表編號為 的點之間有一條權值為 的有向邊,每兩個整數之間用一個空格隔開。
輸出格式:
輸出檔案包含行,每行一個整數代表答案。
說明
【測試資料與約定】 對於不同的測試點,我們約定各種引數的規模不會超過如下
測試點編號 | 是否有0邊 | ||||
---|---|---|---|---|---|
1 | 5 | 5 | 10 | 0 | 否 |
2 | 5 | 1000 | 2000 | 0 | 否 |
3 | 5 | 1000 | 2000 | 50 | 否 |
4 | 5 | 1000 | 2000 | 50 | 否 |
5 | 5 | 1000 | 2000 | 50 | 否 |
6 | 5 | 1000 | 2000 | 50 | 是 |
7 | 5 | 100000 | 200000 | 0 | 否 |
8 | 3 | 100000 | 200000 | 50 | 否 |
9 | 3 | 100000 | 200000 | 50 | 是 |
10 | 3 | 100000 | 200000 | 50 | 是 |
對於 100%的資料, 資料保證:至少存在一條合法的路線。
NOIP2018賽前兩個月 回首2017爆零的噩夢
題目分析
用表示1到u的最短路 用表示1到u長度不超過的路徑總數
記搜時需要在反向圖上跑 若 則 當時,令
對於判0環 只要在記搜時用棧判斷就好 向下搜尋前先記錄二元組入棧 若在搜尋中遇到當前二元組已在棧中,則可判定有0環
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long lt;
int read()
{
int x=0,f=1;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
const int maxn=100010;
int t,n,m,k,p;
struct node{int v,dis,nxt;}E[2][maxn<<1];
int head[2][maxn],tot[2];
int d[maxn],vis[maxn];
priority_queue< pair<int,int> > q;
int dp[maxn][55],ins[maxn][55];
void add(int u,int v,int dis,int d)
{
E[d][++tot[d]].nxt=head[d][u];
E[d][tot[d]].v=v; E[d][tot[d]].dis=dis;
head[d][u]=tot[d];
}
void dij()
{
memset(vis,0,sizeof(vis));
memset(d,111,sizeof(d)); d[1]=0;
q.push(make_pair(0,1));
while(!q.empty())
{
int u=q.top().second; q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[0][u];i;i=E[0][i].nxt)
{
int v=E[0][i].v,dis=E[0][i].dis;
if(d[v]>d[u]+dis)
{
d[v]=d[u]+dis;
q.push( make_pair(-d[v],v) );
}
}
}
}
int DP(int u,int tk)
{
if(ins[u][tk]) return -1;
if(dp[u][tk]) return dp[u][tk];
ins[u][tk]=1;
if(u==1) dp[u][tk]=1;
for(int i=head[1][u];i;i=E[1][i].nxt)
{
int v=E[1][i].v,dis=E[1][i].dis;
if(d[u]+tk-dis-d[v]>=0)
{
int tt=DP(v,d[u]+tk-dis-d[v]);
if(tt==-1) return dp[u][tk]=-1;
dp[u][tk]=(dp[u][tk]+tt)%p;
}
}
ins[u][tk]=0;
return dp[u][tk];
}
void init()
{
tot[0]=tot[1]=0;
memset(head,0,sizeof(head));
memset(dp,0,sizeof(dp));
memset(ins,0,sizeof(ins));
}
int main()
{
t=read();
while(t--)
{
n=read();m=read();k=read();p=read();
init();
for(int i=1;i<=m;++i)
{
int u=read(),v=read(),dis=read();
add(u,v,dis,0); add(v,u,dis,1);
}
dij();
printf("%d\n",DP(n,k));
}
return 0;
}