NOIP 2017提高组复赛试题及解题报告(2)

3. 逛公园(park.cpp/c/pas)

【问题描述】
策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从1号点进去,从N号点出来。

策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果1号点到N号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?

为避免输出过大,答案对P取模。

如果有无穷多条合法的路线,请输出−1。

【输入格式】
输入文件名为 park.in。
第一行包含一个整数T, 代表数据组数。
接下来T组数据,对于每组数据:
第一行包含四个整数N, M, K, P,每两个整数之间用一个空格隔开。
接下来M行,每行三个整数ai , bi , ci,代表编号为ai, bi的点之间有一条权值为ci的有向边,每两个整数之间用一个空格隔开。

【输出格式】
输出文件名为 park.out。
输出文件包含T行,每行一个整数代表答案。

【输入输出样例 1】
park.in
2
5  7 2  10
1  2  1
2  4  0
4  5  2
2  3  2
3  4  1
3  5  2
1  5  3
2  2 0  10
1  2  0
2  1  0
park.out
3
-1

【数据规模与约定】
对于不同的测试点,我们约定各种参数的规模不会超过如下:
NOIP 2017提高组复赛试题及解题报告(2)-少儿编程网

对于100%的数据, 1≤P≤109,1≤ai,bi≤𝑁,0≤ci ≤ 1000。

数据保证:至少存在一条合法的路线。

【题目解答】
最短路+拓扑排序+DP

先跑最短路。发现K只有50,所以一定是要从K入手。所以考虑DP,令f[i][j]表示走到i,多走的长度是j的方案数。(多走指的是比最短路多的部分的长度)。但是发现这个DP方程是存在环的,因为最短路径图上的边以及零边都是可以同行转移的。将最短路径图上的边以及零边都拿出来跑拓扑排序,然后让这些边在转移时必须沿着拓扑序转移即可。特别地,如果一个零环位于一条从1到n长度<=d+K的路径上,则输出-1即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
#include<utility>
#define mp(A,B) make_pair(A,B)
using namespace std;
const int maxn=100010;
const int maxm=200010;
int n,m,cnt,K,P,tot,ans;
int to[maxm],next[maxm],val[maxm],head[maxn],dis[maxn],vis[maxn],d[maxn],q[maxn<<1],f[51][maxn];
int to2[maxm],next2[maxm],val2[maxm],head2[maxn],dis2[maxn];
priority_queue<pair<int,int>> pq;
inline int rd()
{
int ret=0; char gc=getchar();
while(gc<'0'||gc>'9') gc=getchar();
while(gc>='0'&&gc<='9')   ret=ret*10+gc-'0',gc=getchar();
return ret;
}
inline void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt;
to2[cnt]=a,val2[cnt]=c,next2[cnt]=head2[b],head2[b]=cnt++;
}
inline void upd(int &x,int y)
{
x=(x+y)%P;
}
void work()
{
n=rd(),m=rd(),K=rd(),P=rd();
int i,j,k,a,b,c,u;
memset(head,-1,sizeof(head)),memset(head2,-1,sizeof(head2)),cnt=tot=ans=0;
for(i=1;i<=m;i++)    a=rd(),b=rd(),c=rd(),add(a,b,c);
//1
memset(vis,0,sizeof(vis)),memset(dis,0x3f,sizeof(dis)),memset(d,0,sizeof(d));
pq.push(mp(0,1)),dis[1]=0;
while(!pq.empty())
{
u=pq.top().second,pq.pop();
if(vis[u])  continue;
vis[u]=1;
for(i=head[u];i!=-1;i=next[i])  if(dis[to[i]]>dis[u]+val[i])dis[to[i]]=dis[u]+val[i],pq.push(mp(-dis[to[i]],to[i]));
}
//2
memset(dis2,0x3f,sizeof(dis2)),memset(vis,0,sizeof(vis));
pq.push(mp(0,n)),dis2[n]=0;
while(!pq.empty())
{
u=pq.top().second,pq.pop();
if(vis[u])  continue;
vis[u]=1;
for(i=head2[u];i!=-1;i=next2[i])    if(dis2[to2[i]]>dis2[u]+val2[i])dis2[to2[i]]=dis2[u]+val2[i],pq.push(mp(-dis2[to2[i]],to2[i]));
}
//3
for(i=1;i<=n;i++)    for(j=head[i];j!=-1;j=next[j])  if(dis[i]+val[j]==dis[to[j]])   d[to[j]]++;
for(i=1;i<=n;i++)    if(!d[i])  q[++tot]=i;
for(j=1;j<=tot;j++)
{
u=q[j];
for(i=head[u];i!=-1;i=next[i])  if(dis[u]+val[i]==dis[to[i]])
{
d[to[i]]--;
if(!d[to[i]])   q[++tot]=to[i];
}
}
for(i=1;i<=n;i++)   if(d[i]&&dis[i]+dis2[i]<=dis[n]+K)
{
printf("-1n");
return ;
}
//DP
memset(f,0,sizeof(f));
f[0][1]=1;
for(k=0;k<=K;k++)
{
for(i=1;i<=tot;i++)  for(u=q[i],j=head[u];j!=-1;j=next[j])   if(dis[u]+val[j]==dis[to[j]])
{
upd(f[k][to[j]],f[k][u]);
}
for(i=1;i<=n;i++) for(j=head[i];j!=-1;j=next[j]) if(dis[i]+val[j]!=dis[to[j]]&&k+dis[i]+val[j]-dis[to[j]]<=K)
{
upd(f[k+dis[i]+val[j]-dis[to[j]]][to[j]],f[k][i]);
}
}
for(i=0;i<=K;i++)    upd(ans,f[i][n]);
printf("%dn",ans);
}
int main()
{
int T=rd();
while(T--) work();
return 0;
}

本文链接:NOIP 2017提高组复赛试题及解题报告(2)

转载声明:本站文章若无特别说明,皆为原创,转载请注明来源:少儿编程网,谢谢!^^


*文章为作者独立观点,不代表少儿编程网立场
发表评论

坐等沙发
相关文章
CSP和NOIP是什么关系?
CSP和NOIP是什么关系?
中国计算机学会推出的CSP-S/J测试或将被视为替代NOIP
中国计算机学会推出的CSP-S/J测试或将被…
NOIP 2017提高组复赛试题及解题报告(3)
NOIP 2017提高组复赛试题及解题报告(3)
NOIP少年丨黄子桉:不骄不躁不傲慢不强求
NOIP少年丨黄子桉:不骄不躁不傲慢不强求
为什么越来越多家长选择信息学竞赛?
为什么越来越多家长选择信息学竞赛?
孩子学完各个阶段的编程课程能够参加哪些比赛?
孩子学完各个阶段的编程课程能够参加哪…
NOIP2017 作者
NOI官网发布了2017年全国青少年信息学奥林匹克联赛(NOIP2017)的报名通知。NOIP2017将于2017年10月及11月分别举行初赛和复赛,有意向参加比赛的考生,即日起至10月10日可报名。