首页 > 其他分享 >P3953 [NOIP2017 提高组] 逛公园

P3953 [NOIP2017 提高组] 逛公园

时间:2024-04-24 21:13:22浏览次数:25  
标签:NOIP2017 tmp return int ret read 逛公园 P3953 dis

P3953 [NOIP2017 提高组] 逛公园

求有向图中 \(1\) 到 \(n\) 的路径中长度小于等于 \(dis(1,n)+k\) 的方案数。\(dis(1,n)\) 表示最短路。\(k\le 50\)。

部分分 \(k=0\),直接最短路计数即可。

我们发现有向图中存在后效性,不好动态规划,但我们仔细思考后,在不存在 \(0\) 边的情况下,设 \(f(u,k)\) 表示从 \(1\) 走到 \(u\) 时长度为 \(k\) 的方案数,若存在 \(f(v,j)\) 需要转移到它身上,那么有 \(k\ne j\),不存在后效性。所以最简单的:

\[f(u,k)\rightarrow f(v,k+w(u,v)) \]

而这并不好实现,因为我们无法确定 dp 顺序,也就是不能保证有拓扑序。而我们发现 \(k\) 很小,我们只关心 \(dis(1,n)+k\) 以内的路径,并且对于 \(1\) 到 \(u\) 的路径,长度一定大于等于 \(dis(1,u)\)。所以状态写成 \(f(u,k)\) 表示从 \(1\) 走到 \(n\) 时长度为 \(dis(1,u)+k\) 的方案数。

假设 \(f(u,k)\) 可以转移到 \(f(v,x)\),那么有

\(dis(1,u)+k+w(u,v)=dis(1,v)+x\)

\(x=dis(1,u)+k+w(u,v)-dis(1,v)\)

所以 \(f(u,k)\rightarrow f(v,dis(1,u)+k+w(u,v)-dis(1,v))\)

70pts:此时我们转移就可以把按 \(dis\) 值从小到大排序转移(对于无 \(0\) 边)。

100pts:一样有无后效性,因为对于 \(k=x\) 时,\(dis(1,u)+w(u,v)=dis(1,v)\) 为最短路上的拓扑序,所有最短路构成的图是 \(DAG\);而其他情况则是分层图上不同层的转移,同样有无后效性。

但有一个特殊情况就是图中 \(1\) 到 \(n\) 的路径中存在 \(0\) 环,特判一下就可以转移了。可以用记忆化搜索,实现起来细节不多。

总结:特殊性质的优化,变为无后效性问题

#include <bits/stdc++.h>
typedef long long ll;
int read() {
	int x = 0, f = 1;
	char c = getchar();
	while(!isdigit(c)) {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(isdigit(c)) {
		x = (x << 3) + (x << 1) + (c - '0');
		c = getchar();
	} 
	return x * f;
}
bool flg;
int t, n, m, k, p, cnt, cnt2, ans;
int h[100010], h2[100010], dis[100010], vis[100010], f[100010][51], ins[100010][51];
struct node {
	int to, nxt, w;
} e[200010], e2[200010];
void add(int u, int v, int w) {
	e[++cnt].to = v;
	e[cnt].nxt = h[u];
	e[cnt].w = w;
	h[u] = cnt;
}
void add2(int u, int v, int w) {
	e2[++cnt2].to = v;
	e2[cnt2].nxt = h2[u];
	e2[cnt2].w = w;
	h2[u] = cnt;
}
struct ac {
	int v, w;
	friend bool operator < (ac a, ac b) {
		return a.w > b.w;
	}
} tmp;
void dij(int s) {
	std::priority_queue<ac> q;
	for(int i = 1; i <= n; i++) dis[i] = 0x3f3f3f3f, vis[i] = 0;
	dis[s] = 0;
	q.push({s, 0});
	while(!q.empty()) {
		int u = q.top().v;
		q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = h[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if(dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				tmp.v = v, tmp.w = dis[v];
				q.push(tmp);
			}
		}
	}
}
int dp(int u, int j) {
	if(j < 0) return 0;
	if(ins[u][j]) {
		flg = 1;
		return 0;
	}
	if(f[u][j]) return f[u][j];
	ins[u][j] = 1;
	int ret = 0;
	for(int i = h2[u]; i; i = e2[i].nxt) {
		int v = e2[i].to;
		ret = (ret + dp(v, dis[u] + j - e2[i].w - dis[v])) % p;
		if(flg) return 0;
	}
	ins[u][j] = 0;
	return f[u][j] = ret;
}
void Solve() {
	t = read();
	while(t--) {
		n = read(), m = read(), k = read(), p = read();
		for(int i = 1; i <= m; i++) {
			int u = read(), v = read(), w = read();
			add(u, v, w), add2(v, u, w);
		}
		dij(1);
		ans = 0, flg = 0;
		memset(f, 0, sizeof(f));
		memset(ins, 0, sizeof(ins));		
		dp(1, 0);
		f[1][0] = 1;
		for(int i = 0; i <= k; i++) {
			ans = (ans + dp(n, i)) % p;
		}
		if(flg) std::cout << "-1\n";
		else std::cout << ans << "\n";
		for(int i = 1; i <= n; i++) {
			h[i] = h2[i] = 0;
		}
		cnt = cnt2 = 0;
	}
}

int main() {
	
	Solve();

	return 0;
}

标签:NOIP2017,tmp,return,int,ret,read,逛公园,P3953,dis
From: https://www.cnblogs.com/FireRaku/p/18092162

相关文章

  • P3956 [NOIP2017 普及组] 棋盘
    /*这题本身不难,但是我写难了就是一个bfs,没了但是我的写法恰好犯了一个错误hark数据35110211311220330答案是4而我能输出30-1-110-11-10原因是我先走到了(2,2)这时候......
  • [题解] <NOIP2017> 时间复杂度
    [题解]NOIP2017时间复杂度题目描述小明正在学习一种新的编程语言A++,刚学会循环语句的他激动地写了好多程序并给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序,于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正......
  • P3956 [NOIP2017 普及组] 棋盘
    原题链接题解dijkstra算法的应用。相同颜色权值为0;不同颜色权值为1;有颜色到无颜色权值为2。其中不能连续两步走无颜色结点,即该情况需要特别考虑。code #include<bits/stdc++.h>usingnamespacestd;constintMAX=1e9;inta[105][105],dis[105][105],vis[105][105];int......
  • [NOIP2017 提高组] 小凯的疑惑 / [蓝桥杯 2013 省] 买不到的数目
    这肯定是学证明了,看这篇文章补充一下细节首先,\(m\)的范围应该是\([0,b-1]\)然后,当\(m\)取不同值的时候,\(ma\)%\(b\)一定为不同值(这个性质确实有点奇特,可以记下来)反证,如果\(m_1a\equivm_2a\:(mod\:b)\)且\(0≤m_1<m_2≤b-1\),那么就有\(b|(m_2-m_1)a\),题目给出了\(a,b\)互质,......
  • P3957 [NOIP2017 普及组] 跳房子
    原题链接题解二分加动态维护区间最大值注意设立变量的含义,改变变量值的规则code#include<bits/stdc++.h>#definelllonglongusingnamespacestd;llsum[500005]={0};structunit{llx,v;booloperator<(constunit&b)const{returnb.v>v;}//}room[5000......
  • P3958 [NOIP2017 提高组] 奶酪
    原题链接思路并查集然后看看是否存在上表面联通的洞与下表面联通的洞位于同一集合code#include<bits/stdc++.h>usingnamespacestd;doublen,h,r;intfa[1005];vector<int>up,down;struct{doublex,y,z;}hole[1005];doubledis(inti,intj){returnpo......
  • P3957 [NOIP2017 普及组] 跳房子
    50分代码1//P3957[NOIP2017普及组]跳房子2#include<iostream>3#include<queue>4#include<cstring>5#include<cmath>6usingnamespacestd;7typedeflonglongll;8intn,d,k;9inta[500010][2];10llf[500010],sum=0;11boo......
  • P3953 [NOIP2017 提高组] 逛公园
    Description策策同学特别喜欢逛公园。公园可以看成一张\(N\)个点\(M\)条边构成的有向图,且没有自环和重边。其中\(1\)号点是公园的入口,\(N\)号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。策策每天都会去逛公园,他总是从\(1\)号点进去,从\(N\)......
  • P3956 [NOIP2017 普及组] 棋盘
    传送门P3956[NOIP2017普及组]棋盘不清楚曾师为什么把这个神奇的题目放在搜索\(search\)专栏,反正我用\(dijkstra\)水过去了,虽然\(dijkstra\)严格来说也是一种能够解决一般性最短路问题的算法。然后考虑这道题的建图。这道题来看首先是去除魔法的部分,一般地,任意一个点只......
  • NOIP2017提高组初赛易错题解析
     8.由四个不同的点构成的简单无向连通图的个数是()A.32 B.35 C.38 D.41错误原因:数重了正解:分情况计算,6条边的有1种,5条边的有C(6,1)=6种,4条边的有C(6,4)=15种,3条边,要分度数,2+2+1+1的有12种,3+1+1+1的有4种,共38种 10.若 f0​=0,f1​=1,fn+1​=(fn​+fn−1)/2​​,则随着......