来源:Luogu P1685,JZOJ #333
题目描述
顺利通过了黄药师的考验,下面就可以尽情游览桃花岛了!
你要从桃花岛的西头开始一直玩到东头,然后在东头的码头离开。可是当你游玩了一次后,发现桃花岛的景色实在是非常的美丽!!!于是你还想乘船从桃花岛东头的码头回到西头,再玩一遍,但是桃花岛有个规矩:你可以游览无数遍,但是每次游玩的路线不能完全一样。
我们把桃花岛抽象成了一个图,共 nnn 个点代表路的相交处,mmm 条边表示路,边是有向的(只能按照边的方向行走),且可能有连接相同两点的边。输入保证这个图没有环,而且从西头到东头至少存在一条路线。两条路线被认为是不同的当且仅当它们所经过的路不完全相同。
你的任务是:把所有不同的路线游览完一共要花多少时间?
解题思路
- 这是一道较为综合的题,囊括拓扑排序和递推,是一道经典的好题;
- 首先,求一个拓扑序,存在队列 qqq 中,然后在暴力枚举递推一遍,Total[i]Total[i]Total[i] 表示走到第 iii 个点的方案数,Times[i]Time_s[i]Times[i] 表示走到第 iii 个点花费的时间;
- 可得两个递推公式
Total[y]=(Total[x]+Total[y]) % Mod;
Time_s[y]=(Time_s[x]+Time_s[y]+Total[x]*e[j].v) % Mod;
代码君
#include <bits/stdc++.h>
using namespace std;
const int Mod=10000;
int tot=0,n,m,st,en,Time;
int linkk[50010],q[50010],Total[50010],Time_s[50010],in[50010];
struct node
{
int y,v,next;
}e[50010];
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline void write(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9) write(x/10);
putchar(x%10+'0');
}
void insert(int x,int y,int v) //邻接表
{
e[++tot].y=y; e[tot].v=v;
e[tot].next=linkk[x]; linkk[x]=tot;
}
void Topsort() //拓扑排序
{
int head=1,tail=0;
for (int i=1;i<=n;i++)
if (in[i]==0) q[++tail]=i; //初始化
for (head=1;head<=tail;head++)
{
int x=q[head]; //取出队头
for (int i=linkk[x];i;i=e[i].next) //邻接表查询
{
int y=e[i].y;
if (--in[y]==0) q[++tail]=y; //入队
}
if (tail==n) return;
}
}
void Recurrence()
{
Total[st]=1; Time_s[st]=0;
for (int i=1;i<=n;i++)
{
int x=q[i];
for (int j=linkk[x];j;j=e[j].next)
{
int y=e[j].y;
Total[y]=(Total[x]+Total[y]) % Mod; //转移方程
Time_s[y]=(Time_s[x]+Time_s[y]+Total[x]*e[j].v) % Mod;
}
}
printf("%d",(Time_s[en]+(Total[en]-1)*Time) % Mod); //最后别忘了乘Time
}
int main()
{
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
scanf("%d %d %d %d %d",&n,&m,&st,&en,&Time);
for (int i=1;i<=m;i++)
{
int x,y,v;
scanf("%d %d %d",&x,&y,&v);
insert(x,y,v);
in[y]++; //入度
}
Topsort();
Recurrence();
return 0;
}