• Luogu P3953 逛公园


    不管怎么说,这都是一道十分神仙的NOIp题

    你可以说它狗,但不可以否认它就是NOIp的难度

    首先这道题很显然是道图论还是一道图论三合一(最短路+拓扑+图上DP)

    先考虑最短路,我们分别以(1)(n)为起点得出与其它点的最短路(虽然NOIp应该不会卡SPFA,但还是建议写稳定的DJ)

    我们先考虑把(-1)的情况给判掉,分析一下发现此时必定有0环(此时可以在0环上无限刷路径)

    但是要注意一下,当且仅当0环上的任意一点(i)(1)的最短路(dis_{1,i})以及它到(n)的最短路(dis_{i,n})满足(dis_{1,i}+dis_{i,n}<=dis_{1,n}+k),因为这个0环首先得在可行路径上

    处理0环的具体操作只要把0边权的边单独拎出来做拓扑排序就好了。

    然后考虑如何统计答案,我们发现(k<=50)。因此我们图上DP计数

    (f_{i,j})表示到第(i)个点时,偏移量(偏移量为当前路径长度与(dis_{1,i})的差)为(j)的方案数:

    我们再枚举(i)可以到达的点(p),若(dis_{1,i}+w+j-dis_{1,p}<=k),然后就可以用(f_{i,j})更新(f_{p,dis_{1,i}+w+j-dis_{1,p}})了((w)(i ightarrow p)的边权)

    最后要注意的是枚举的状态顺序,我们发现对于所有的点(i),当它的(dis_{1,i})越小时,它越早更新

    特别地,当都是0权的边排序时,应该按之前的拓扑顺序排

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int N=100005,K=55;
    struct edge
    {
    	int to,next,v;
    }e[N<<1],re[N<<1];
    struct Zero_edge
    {
    	int to,next;
    }ze[N<<1];
    struct Small
    {
    	int num,s;
    	bool operator <(const Small x) const { return x.s<s; } 
    };
    struct data
    {
    	int d,id,num;
    }a[N];
    int head[N],rhead[N],zhead[N],t,n,m,k,p,x,y,z,cnt,zcnt,ru[N],q[N],f[N][K],dis[N],INF,r[N];
    bool vis[N];
    priority_queue <Small> small;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; while (!isdigit(ch=tc()));
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    inline void clear(void)
    {
    	memset(head,-1,sizeof(head)); memset(rhead,-1,sizeof(rhead));
    	memset(zhead,-1,sizeof(zhead)); memset(ru,0,sizeof(ru)); cnt=zcnt=0;
    }
    inline void add(int x,int y,int z)
    {
    	e[++cnt].to=y; e[cnt].next=head[x]; e[cnt].v=z; head[x]=cnt;
    }
    inline void radd(int x,int y,int z)
    {
    	re[cnt].to=y; re[cnt].next=rhead[x]; re[cnt].v=z; rhead[x]=cnt;
    }
    inline void zadd(int x,int y)
    {
    	ze[++zcnt].to=y; ze[zcnt].next=zhead[x]; zhead[x]=zcnt; ++ru[y];
    }
    inline void top_sort(void)
    {
    	register int i,H=0,T=0;
    	for (i=1;i<=n;++i)
    	{
    		if (!ru[i]) q[++T]=i,a[i].id=T; a[i].num=i;
    	}
    	while (H<T)
    	{
    		int now=q[++H];
    		for (i=zhead[now];~i;i=ze[i].next)
    		if (!(--ru[ze[i].to])) q[++T]=ze[i].to,a[ze[i].to].id=T;
    	}
    }
    inline void front_dijkstra(void)
    {
    	memset(dis,63,sizeof(dis)); memset(vis,0,sizeof(vis));
    	small.push((Small){1,0}); INF=dis[0]; dis[1]=a[1].d=0;
    	while (!small.empty())
    	{
    		int now=small.top().num; small.pop();
    		if (vis[now]) continue; vis[now]=1;
    		for (register int i=head[now];~i;i=e[i].next)
    		if (dis[e[i].to]>dis[now]+e[i].v)
    		{
    			a[e[i].to].d=dis[e[i].to]=dis[now]+e[i].v;
    			small.push((Small){e[i].to,dis[e[i].to]});
    		}
    	}
    }
    inline void back_dijkstra(void)
    {
    	memset(dis,63,sizeof(dis)); memset(vis,0,sizeof(vis));
    	small.push((Small){n,0}); dis[n]=0;
    	while (!small.empty())
    	{
    		int now=small.top().num; small.pop();
    		if (vis[now]) continue; vis[now]=1;
    		for (register int i=rhead[now];~i;i=re[i].next)
    		if (dis[re[i].to]>dis[now]+re[i].v)
    		{
    			dis[re[i].to]=dis[now]+re[i].v;
    			small.push((Small){re[i].to,dis[re[i].to]});
    		}
    	}
    }
    inline bool check(void)
    {
    	for (register int i=1;i<=n;++i)
    	if (ru[i]&&a[i].d+dis[i]<=a[n].d+k) return 1;
    	return 0;
    }
    inline bool cmp(data a,data b)
    {
    	if (a.d<b.d) return 1;
    	if (a.d>b.d) return 0;
    	return a.id<b.id;
    }
    inline void inc(int &x,int y)
    {
    	if ((x+=y)>=p) x-=p;
    }
    inline int DP(void)
    {
    	memset(f,0,sizeof(f)); f[1][0]=1; int ans=0;
    	for (register int s=0;s<=k;++s)
    	{
    		for (register int i=1;i<=n;++i)
    		{
    			int now=a[i].num;
    			for (register int j=head[now];~j;j=e[j].next)
    			if (a[i].d+s+e[j].v-a[r[e[j].to]].d<=k) 
    			inc(f[e[j].to][a[i].d+s+e[j].v-a[r[e[j].to]].d],f[now][s]);
    		}
    		inc(ans,f[n][s]);
    	} return ans;
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	register int i; read(t);
    	while (t--)
    	{
    		read(n); read(m); read(k); read(p); clear();
    		for (i=1;i<=m;++i)
    		{
    			read(x); read(y); read(z);
    			add(x,y,z); radd(y,x,z); if (!z) zadd(x,y);
    		}
    		top_sort(); front_dijkstra(); back_dijkstra();
    		if (check()) { puts("-1"); continue; } sort(a+1,a+n+1,cmp);
    		for (i=1;i<=n;++i) r[a[i].num]=i;
    		write(DP()); putchar('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    activiti app 6.0 乱码
    扫盲--CRM系统和ERP系统的区别
    GraphQL实战-第四篇-构建开发框架
    GraphQL实战-第三篇-spring Boot 实现
    GraphQL实战-第二篇-java实现及分析
    GraphQL实战-第一篇-GraphQL介绍
    @PostConstruct及跳坑记录
    jmeter元件的介绍
    Docker之5. 容器的操作
    Docker之4. 镜像的操作
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9337052.html
Copyright © 2020-2023  润新知