luogu3953 [NOIp2017]逛公园 (tarjan+dijkstra+记忆化搜索)

先跑一边dijkstra算出从1到i的最短距离dis[i]

然后建反向边 从n开始记忆化搜索,(p,k)表示1到p的距离=dis[p]+k的方案数

答案就是$\sum\limits_{i=0}^{k}{(n,i)}$

考虑0环,如果我记搜的时候搜到了0环,那答案就是-1,可以先用tarjan处理一下0边 看看有哪些点在零环上

(其实也可以开个栈 做到(p,k)的时候看(p,k)是不是已经在栈中了 如果是那就是-1)

  1 #include<bits/stdc++.h>
  2 #define CLR(a,x) memset(a,x,sizeof(a))
  3 using namespace std;
  4 typedef long long ll;
  5 typedef pair<int,int> pa;
  6 const int maxn=1e5+10,maxm=2e5+10,maxk=55;
  7 
  8 inline ll rd(){
  9     ll x=0;char c=getchar();int neg=1;
 10     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
 11     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
 12     return x*neg;
 13 }
 14 
 15 struct Edge{
 16     int b,l,ne;
 17 }eg[maxm],neg[maxm];
 18 struct Node{
 19     int p,k,n;
 20     Node(int a=0,int b=0,int c=0){p=a,k=b,n=c;}
 21 };
 22 int egh[maxn],negh[maxn],ect,nect;
 23 int dis[maxn],f[maxn][maxk];
 24 int dfn[maxn],tot,low[maxn],stk[maxn],sh;
 25 int N,M,K,P;
 26 bool in0[maxn],instk[maxn],flag[maxn];
 27 
 28 inline void adeg(int a,int b,int c){
 29     eg[++ect].b=b,eg[ect].l=c,eg[ect].ne=egh[a],egh[a]=ect;
 30     neg[++nect].b=a,neg[nect].l=c,neg[nect].ne=negh[b],negh[b]=nect;
 31 }
 32 
 33 void tarjan(int x){
 34     dfn[x]=low[x]=++tot,instk[x]=1,stk[++sh]=x;
 35     for(int i=egh[x];i;i=eg[i].ne){
 36         if(eg[i].l) continue;
 37         int b=eg[i].b;
 38         if(!dfn[b]) tarjan(b),low[x]=min(low[x],low[b]);
 39         else if(instk[b]) low[x]=min(low[x],dfn[b]);        
 40     }
 41     if(dfn[x]==low[x]){
 42         int n=0;
 43         for(int i=sh;stk[i]!=x;i--) n++; 
 44         while(1){
 45             instk[stk[sh]]=0;
 46             in0[stk[sh]]=n>0;
 47             if(stk[sh--]==x) break;
 48         }
 49     }
 50 }
 51 
 52 priority_queue<pa,vector<pa>,greater<pa> > q;
 53 inline void dijkstra(){
 54     while(!q.empty()) q.pop();
 55     CLR(flag,0);CLR(dis,63);dis[1]=0;
 56     q.push(make_pair(0,1));
 57     while(!q.empty()){
 58         int p=q.top().second;q.pop();
 59         if(flag[p]) continue;
 60         flag[p]=1;
 61         for(int i=egh[p];i;i=eg[i].ne){
 62             int b=eg[i].b;
 63             if(dis[b]>dis[p]+eg[i].l){
 64                 dis[b]=dis[p]+eg[i].l;
 65                 q.push(make_pair(dis[b],b));
 66             }
 67         }
 68     }
 69 }
 70 
 71 inline int solve(int x,int y){
 72     // if(f[x][y]>=0) printf("get%d %d %d\n",x,y,f[x][y]);
 73     if(y<0||y>K) return 0;
 74     if(in0[x]) return -1;
 75     if(f[x][y]>=0) return f[x][y];
 76     f[x][y]=0;
 77     for(int i=negh[x];i;i=neg[i].ne){
 78         int b=neg[i].b;
 79         int re=solve(b,dis[x]-dis[b]+y-neg[i].l);
 80         if(re==-1) return -1;
 81         f[x][y]+=re,f[x][y]%=P;
 82     }
 83     return f[x][y];
 84 }
 85 
 86 int main(){
 87     int i,j,k;
 88     for(int T=rd();T;T--){
 89         N=rd(),M=rd(),K=rd(),P=rd();
 90         CLR(egh,0);ect=0;
 91         CLR(negh,0);nect=0;
 92         for(i=1;i<=M;i++){
 93             int a=rd(),b=rd(),c=rd();
 94             adeg(a,b,c);
 95         }
 96         CLR(dfn,0);CLR(instk,0);tot=0;CLR(in0,0);
 97         for(i=1;i<=N;i++)
 98             if(!dfn[i]) tarjan(i);
 99         dijkstra();
100         CLR(f,-1);
101         f[1][0]=1;int ans=0;
102         for(i=0;i<=K;i++)
103             ans+=solve(N,i),ans%=P;
104         printf("%d\n",ans<0?-1:ans);
105     }
106     
107     return 0;
108 }

 

posted @ 2018-11-02 16:35  Ressed  阅读(552)  评论(0编辑  收藏  举报