最短路+拓撲排序+dp NOIP 2017 逛公園
讓我們一起來%forever_shi神犇
題意:
給你一個
個點
條邊的有向帶權圖,設
號點到
號點的最短路是
題解:
顯然我們要先處理出最短路,如果
,就是最短路計數了。要做計數,我們不難想到要在圖上
。我們發現只要有一個邊權全部是
的環,那麼我們的滿足題意的路徑就會有無數條,因為可以在環裡轉任意多圈之後再出來。那麼我們要判斷是否有無窮多解就是去找有沒有全部是
的環。
我們可以先處理出一個最短路圖,最短路圖的含義是由所有 的邊連成的圖,最短路圖的一個性質:如果邊權都是正數,那麼最短路圖是一個DAG。DAG是可以拓撲排序的,如果最後每個點入度都是 ,就不存在權值全是 的環,否則就是有權值全是 的環,因為權值是 的邊一定不會讓兩點之間的最短路變長,所以其就一定會在最短路圖上,而形成環的話是沒法有其中某一個點的入度是 ,因此判斷拓撲排序後的入度即可。
對圖拓撲排序後根據圖的拓撲序在DAG上 也是一個經典套路,這裡我們就會採用這個套路。我們設 表示對於點 比 大 的路徑數,那麼對於路徑 ,狀態轉移方程即為: 。
那麼我們列舉 ,然後按照拓撲序列舉 ,再列舉從 出發的所有邊,進行 。注意外層是列舉 ,因為在 的過程中如果外層列舉 的話DAG上是沒有環的,但是這裡的邊是列舉原圖的邊,所以可能之後會有環再回到 ,得到答案就是錯誤的了。還有就是這個 看似列舉的層數很多,但是其實複雜度並不高,因為所有邊都只會被列舉到一次,所有點都只會被列舉 次,所以總的複雜度是 的。
最後答案就是
(吸氧苟過去QAQ)
#include<bits/stdc++.h>
#define ll long long
#define rint register int
using namespace std;
const int maxn=201000;
struct node
{
int next,to,dis,from;
}e[maxn],a[maxn];
int head[maxn],head1[maxn],num,num1,i;
int dis[maxn],n,m,T,K,p,rd[maxn],numm[maxn];
int que[maxn],ji,u,v,d;
ll dp[200200][55],ans;
bool book[maxn];
priority_queue<pair<int,int> > q;
int read()
{
int x=0,y=1;
char c;
c=getchar();
while((c<'0'||c>'9')&&c!='0')
c=getchar();
if(c=='-')
{
y=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
return x*y;
}
void add(int from,int to,int dis)
{
e[++num].next=head[from];
e[num].to=to;
e[num].from=from;
e[num].dis=dis;
head[from]=num;
}
void add1(int from,int to,int dis)
{
a[++num1].next=head1[from];
a[num1].to=to;
a[num1].dis=dis;
head1[from]=num1;
}
void dij(int s)
{
for(rint i=1;i<=n;++i)
dis[i]=999999999;
dis[s]=0;
q.push(make_pair(0,s));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(book[x])
continue;
book[x]=1;
for(rint i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(dis[v]>dis[x]+e[i].dis)
{
dis[v]=dis[x]+e[i].dis;
q.push(make_pair(-dis[v],v));
}
}
}
}
void topsort()
{
int h=1,t=0;
for(rint i=1;i<=n;++i)
if(!rd[i])
que[++t]=i;
while(h<=t)
{
int x=que[h];
numm[++ji]=x;
for(rint i=head1[x];i;i=a[i].next)
{
int v=a[i].to;
rd[v]--;
if(!rd[v])
que[++t]=v;
}
h++;
}
}
int main()
{
cin>>T;
while(T--)
{
memset(book,0,sizeof(book));
memset(head,0,sizeof(head));
memset(head1,0,sizeof(head1));
memset(a,0,sizeof(a));
memset(e,0,sizeof(e));
memset(rd,0,sizeof(rd));
memset(dp,0,sizeof(dp));
memset(numm,0,sizeof(numm));
memset(que,0,sizeof(que));
num=0,num1=0,ji=0,ans=0;
bool flag=0;
scanf("%d%d%d%d",&n,&m,&K,&p);
int u,v,d;
for(rint i=1;i<=m;++i)
{
u=read();
v=read();
d=read();
add(u,v,d);
}
dij(1);
for(rint i=1;i<=num;++i)
{
if(dis[e[i].from]+e[i].dis==dis[e[i].to])
{
add1(e[i].from,e[i].to,e[i].dis);
rd[e[i].to]++;
}
}
topsort();
for(rint i=1;i<=n;++i)
if(rd[i])
{
flag=1;
break;
}
if(flag)
{
printf("-1\n");
continue;
}
dp[1][0]=1;
for(rint k=0;k<=K;++k)
for(rint i=1;i<=n;++i)
{
int x=numm[i];
for(rint j=head[x];j;j=e[j].next)
{
int v=e[j].to;
int len=k+e[j