• P3953 逛公园


    P3953 逛公园

    欢迎hack!毕竟这题被hack的人太多了,我也有可能出错,希望大家带着批判的眼光看这篇题解。

    看完题就应该发现那个 (k) 特别小,显然可以利用

    关于 (-1) ,其实不是很好处理。非常显然我们需要判断的是“是否在一条满足题意(可以从 (1)(n))的路径上存在 (0) 环”,很多错解都是因为对于“满足题意的路径”限制方法出错然后挂掉的。

    考虑对于每个节点开一个 (dp[i][j]) 表示:从这个点开始走,比最短路多走了 (j) 的长度的方案数

    可以先一遍 (Dijsktra) 跑出从 (1) 到每个点的最短路 (dis[i])

    考虑从 (1) 开始记搜出 (dp[1][i]) 就是答案

    其实原本是可以直接dp的,但是记搜过程可以顺便判 (0) 环,不然还得再写一个 (dfs) 判环。更重要的是,我们要找的是“可以到达的点上是否存在 (0) 环”,那么在记搜过程中的体现就是访问到了这个点,省去一些判断,也可以少访问一些节点。

    • (0) 环:对于这个状态是否在栈中搜索开个 bool 记录一下,如果访问到状态 (i,j) 并且 (ins[i][j]) 为真的情况那么必然存在 (0) 环,因为它绕了一圈但是比最短路多的长度还是 (j) 。碰到 (0) 环直接退出即可

    • (dp):考虑从 (u) 走到 (v) 会多产生多少贡献。原本走到 (v)(dis[v]) 的长度,现在是 (dis[u]+w[i]) ,多走了 (dis[u]+w[i]-dis[v]) ,那么还可以多走 (lft-(dis[u]+w[i]-dis[v])) ,即 (dp[u][lft] o dp[v][lft-(dis[u]+w[i]-dis[v])])

    我就是这么写的,然而在洛谷被 hack 了,CCF的数据挺水的,就水过去了 所以这是我在考场上能AC的意思?

    hack数据长这样:

    Input
    1
    4 5 0 10000
    1 2 10
    2 1 10
    2 3 0
    3 2 0
    1 4 100
    Output
    1
    

    像我那样写会被hack的原因在于我对于能到达的限制出现了问题,数据构造了一个死胡同 (1 o2 o3) ,并且 (2 o 3 o 2)(0) 环,但是并不能到 (n)

    接下去考虑解决这个漏洞。

    发现只要在反图上跑就能避免这个问题了,即从 (n) 跑到 (1) ,因为我们最短路是从 (1) 开始记录的,如果在 (n)(1) 的路径上出了个“岔子”或“死胡同”,最短路一定不会往那里延伸,那么就能完美地解决这个问题了!

    注意 dp 方程也要相应改变。

    话说我这代码怎么跑这么快qwq,最优解第三,没卡过常呢,正常写的代码。

    upd:卡完常变慢了/kk

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return f?x:-x;
    }
    #define N 100005
    #define M 200005
    #define K 55
    int n,m,k,mod,ans,flg,dis[N],dp[N][K];
    bool vis[N],ins[N][K];
    void fmod(int&x){x-=mod,x+=x>>31&mod;}
    struct edge{
    	int nxt,to,val;
    }e[M],E[M];
    int head[N],num_edge,Head[N],Num_edge;
    void addedge(int fr,int to,int val){
    	++num_edge;
    	e[num_edge].nxt=head[fr];
    	e[num_edge].val=val;
    	e[num_edge].to=to;
    	head[fr]=num_edge;
    }
    void Addedge(int fr,int to,int val){
    	++Num_edge;
    	E[Num_edge].nxt=Head[fr];
    	E[Num_edge].val=val;
    	E[Num_edge].to=to;
    	Head[fr]=Num_edge;
    }
    void clear(){
    	ans=0;
    	memset(dp,-1,sizeof(dp));
    	memset(head,0,sizeof(head));
    	memset(Head,0,sizeof(Head));
    	num_edge=Num_edge=0,flg=1;
    }
    struct Dij{
    	int u,dis;
    	Dij(int u_,int d_){u=u_,dis=d_;}
    	inline bool operator < (const Dij&t)const{return dis>t.dis;}
    };
    priority_queue<Dij>q;
    void dij(){
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	q.push(Dij(1,dis[1]=0));
    	while(!q.empty()){
    		Dij now=q.top();q.pop();
    		int u=now.u;
    		if(vis[u])continue;
    		vis[u]=1;
    		for(int i=head[u];i;i=e[i].nxt){
    			int v=e[i].to;
    			if(dis[v]>dis[u]+e[i].val){
    				dis[v]=dis[u]+e[i].val;
    				if(!vis[v])q.push(Dij(v,dis[v]));
    			}
    		}
    	}
    }
    int dfs(int u,int lft){
    	if(!flg)return 0;
    	if(ins[u][lft])return flg=0;
    	if(~dp[u][lft])return dp[u][lft];
    	int res=0;
    	ins[u][lft]=1;
    	for(int i=Head[u];i&&flg;i=E[i].nxt){
    		int v=E[i].to;
    		int w=lft-(E[i].val-(dis[u]-dis[v]));
    		if(w>=0)fmod(res+=dfs(v,w));
    	}
    	ins[u][lft]=0;
    	if(u==1&&!lft)++res;
    	return dp[u][lft]=res;
    }
    void Main(){
    	clear();
    	n=read(),m=read(),k=read(),mod=read();
    	for(int i=1;i<=m;++i){
    		int x=read(),y=read(),z=read();
    		addedge(x,y,z),Addedge(y,x,z);
    	}
    	dij();
    	for(int i=0;flg&&i<=k;++i)fmod(ans+=dfs(n,i));
    	flg?printf("%d
    ",ans):puts("-1");
    }
    signed main(){for(int T=read();T;--T)Main();}
    
    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    Visual Prolog 的 Web 专家系统 (7)
    spring来源理解-BeanFactory子类XmlBeanFactory创建过程
    Threejs 它可以在建立其内部房间效果可见
    Android AlarmManager报警的实现
    什么是注册表和注册表中的作用
    精致的外观Request
    数据仓库的工作日记_创纪录的(一)
    servlet api.jar是干什么的?
    servlet-api-2.5.jar
    用poi-3.6-20091214.jar 实现java给excel资料加密
  • 原文地址:https://www.cnblogs.com/zzctommy/p/13871032.html
Copyright © 2020-2023  润新知