1. 程式人生 > >luoguP3953 逛公園 最短路計數 拓撲序

luoguP3953 逛公園 最短路計數 拓撲序

luoguP3953 逛公園

題目傳送門

分析1

作為一名標準的NOIP退役選手,果然過了一年之後仍然不會做這道題。
首先肯定先求最短路,一種思路是 f [ k ] [ u ]

f[k][u] 表示鬆弛了 k k 的最短路從起點走到 u u 節點。
很容易寫出方程:
f
[ k ] [ u ] > f [ k
+ w + d i s [ u ] d i s [ v ] ] [ v ] f[k][u]->f[k+w+dis[u]-dis[v]][v]

其中 k + w + d i s [ u ] k+w+dis[u] 表示的是這一回走到 v v 的步數,減去原來的最短路 d i s [ v ] dis[v] 就是走到 v v 的鬆弛步數。
由於最短路的性質,所以 w + d i s [ u ] d i s [ v ] 0 w+dis[u]-dis[v]\ge 0 所以這個 D p Dp 的轉移實際上形成了一張分層圖。我們從小到大列舉 k k 這樣就可以保證轉移的拓撲有序。
但是要注意一種 w + d i s [ u ] d i s [ v ] = 0 w+dis[u]-dis[v]= 0 的情況。這意味著 u > v u->v 這條邊在最短路徑中。也就是說,對於所有的在最短路徑上的邊,都需要特殊考慮其拓撲序。
首先在沒有 0 0 邊的情況,最短路徑邊一定形成了一張 D A G DAG ,到1的距離 d i s 1 dis_1 就是他們的拓撲偏序。按照 d i s 1 dis_1 排序即可轉移。
考慮 0 0 邊的情況。如果有 0 0 環,判斷環中的節點是不是合法的。合法定義為 d i s 1 [ i ] + d i s n [ i ] d i s 1 [ n ] + K dis_1[i]+dis_n[i]\ge dis_1[n]+K ,如果這樣的話,方案數一定無窮,輸出-1即可。否則的話,這個 0 0 環中的所有節點一定不會出現在我們方程的轉移中,可以不用管他們。
這個時候剩下的圖一定仍然是一張 D A G DAG 。只不過我們不能僅僅按照 d i s 1 dis_1 排序,考慮 d i s 1 dis_1 相同的節點,還需要考慮 0 0 邊的順序。所以對於所有 0 0 邊做一遍拓撲排序,以 d i s 1 dis_1 為第一關鍵字, 0 0 邊拓撲序為第二關鍵字重新排序即可。
複雜度 O ( K M ) O(KM)

程式碼1

#include<bits/stdc++.h>
const int N = 1e5 + 10, M = 2e5 + 10, Nt = 131072, inf = 0x3f3f3f3f;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, q[N], d[N], rk[N], f[N], g[N], *D, id[N], T[Nt << 1], dp[51][N], K, P;
struct Edge {
	int nx[M], pr[N], to[M], w[M], tp;
	void add(int u, int v, int W) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp; w[tp] = W;}
	void adds(int u, int v, int w) {add(u, v, w); add(v, u, w);}
	void Pre() {for(int i = 1;i <= n; ++i) pr[i] = 0; tp = 0;}
}G, R;
int min(int a, int b) {return D[a] < D[b] ? a : b;}
bool cmp(int a, int b) {return f[a] == f[b] ? rk[a] < rk[b] : f[a] < f[b];}
void Up(int i, int v) {for(T[i += Nt] = v;i >>= 1;) T[i] = min(T[i << 1], T[i << 1 | 1]);}
void Dij(const Edge &G, int st) {
	for(int i = 0;i <= n; ++i) D[i] = inf;
	D[st] = 0; Up(st, st);
	for(;D[T[1]] != inf;) {
		int u = T[1]; Up(u, 0);
		for(int i = G.pr[u], v, w; i; i = G.nx[i])
			if(D[v = G.to[i]] > (w = D[u] + G.w[i]))
				D[v] = w, Up(v, v);
	}
}
bool Topsort() {
	int L = 1, R = 0;
	for(int i = 1;i <= n; ++i) 
		if(!d[i]) 
			q[++R] = i;
	for(int u = q[L];L <= R; u = q[++L])
		for(int i = G.pr[u]; i; i = G.nx[i])
			if(!G.w[i] && !--d[G.to[i]]) 
				q[++R] = G.to[i];
	for(int i = 1;i <= R; ++i) 
		rk[q[i]] = i;
	for(int i = 1;i <= n; ++i)
		if(d[i] && f[i] + g[i] <= f[n] + K)
			return false;
	for(int i = 1;i <= n; ++i) 
		id[i] = i;
	std::sort(id + 1, id + n + 1, cmp);
	return true;
}
void Inc(int &a, int b) {a += b; if(a >= P) a -= P;}
void Dp() {
	memset(dp, 0, sizeof(dp));
	dp[0][id[1]] = 1;
	for(int k = 0;k <= K; ++k)
		for(int x = 1, u = id[1];x <= n; u = id[++x]) 
			for(int i = G.pr[u], w, v; i; i = G.nx[i]) 
				if((w = f[u] + k + G.w[i] - f[v = G.to[i]]) <= K)
					Inc(dp[w][v], dp[k][u]);
}
int main() {
	for(int C = ri();C--;) {
		n = ri(); int m = ri(); K = ri(), P = ri();
		G.Pre(); R.Pre();
		for(int i = 1;i <= n; ++i) 
			d[i] = rk[i] = 0;
		for(int u, v, w;m--
            
           

相關推薦

luoguP3953 公園 短路計數

luoguP3953 逛公園 題目傳送門 分析1 作為一名標準的NOIP退役選手,果然過了一年之後仍然不會做這道題。 首先肯定先求最短路,一種思路是 f

[NOIP2017]公園 短路DP

none img ble next lose spf 沒有 gist pop ~~~題面~~~ 題解:   挺好的一道題。   首先我們將所有邊反向,跑出n到每個點的最短路,然後f[i][j]表示從i號節點出發,路徑長比最短路大j的方案數。   觀察到,如果圖中出現

洛谷3953 NOIP2017 公園 短路圖+排序+dp

題目連結 題意: 給你一個n個點m條邊的有向帶權圖,設1號點到n號點的最短路是dis,給你一個k(k<=50),求所有1到n的路徑中長度不超過dis+k的數量。 題解: 顯然我們要先處理出最短路,以後再也不會寫SPFA了,因為NOI2018卡了SP

【題解】洛谷P3953[NOIP2017]公園 短路+排序+計數類DP

題目連結 學習了大佬題解。根據大佬的講解,把對應部分分的程式碼打到一起了。(有點臃腫) #pragma GCC optimize(2) #include<cstdio> #include<cstring> #include<

【NOIP2017】公園 短路+DP

() next sizeof truct c++ pre 長度 div 分層圖 誒,去年場上不會處理$0$的環,只拿了$60$有點可惜。 我們先不管邊邊權為$0$的邊。 我們先跑一次最短路,令$dis[u]$表示從$1$至$u$的最短路的長度。 那麽根據題目的要求,從

p1242 字典小的

題目 描述 Description 給定一個有N個節點的有向圖(編號為0~N-1),求其拓撲排序的最小字典序。 輸入格式 Input Format 第一行兩個整數 N和M,表示圖有N個點,M條邊。 接下來M行,2個整數ui和vi,表示ui到vi有條有向邊。 輸出格式 Output

2018.11.07【NOIP2017】【洛谷P3953】公園(DP)(魔改短路計數

傳送門 解析: 首先這鬼畜的最短路肯定你是要自己跑一遍的。 但是我是在反圖上面跑。。 因為我的DP策略表示在當前點 u u

P1144 短路計數

prior print 如果 cstring 不同的 priority %d 2個 name 題目描述 給出一個N個頂點M條邊的無向無權圖,頂點編號為1~N。問從頂點1開始,到其他每個點的最短路有幾條。 輸入輸出格式 輸入格式: 輸入第一行包含2個正整數

洛谷 P1144 短路計數

-- 多少 pop fine www pty class 接下來 輸出格式 題目描述 給出一個N個頂點M條邊的無向無權圖,頂點編號為1~N。問從頂點1開始,到其他每個點的最短路有幾條。 輸入輸出格式 輸入格式: 輸入第一行包含2個正整數N,M,為圖的頂點數與邊

洛谷——P1144 短路計數

lin can 初始化 onclick 初始 () clas har 變形 P1144 最短路計數 題目描述 給出一個N個頂點M條邊的無向無權圖,頂點編號為1~N。問從頂點1開始,到其他每個點的最短路有幾條。 輸入輸出格式 輸入格式: 輸入第一行包含2個正整

BZOJ 2118 墨墨的不等式 數論 + 短路 + 計數

ace ret space log const 最短 fin push bzoj 1 #include<bits/stdc++.h> 2 #define LL long long 3 const LL INF = 50000000000000000ll;

短路計數 [LuoGu P 1144]

memset == insert const size node 分享 pan 備忘 題目大意 : 給出一個N個頂點M條邊的無向無權圖,頂點編號為1~N。問從頂點1開始,到其他每個點的最短路有幾條。 大水題 , 為了備忘 , 還是記下來吧 . 不推方程了,,,,i

洛谷P1144——短路計數

string class 代碼 main oid iostream mes include 計數 題目:https://www.luogu.org/problemnew/show/P1144 spfa跑最短路的同時記錄cnt數組表示到達方案數。 代碼如下: #includ

武漢大學校賽 tarjan+求長鏈+排序

unique void scanf ont bool back 校賽 using -- #include<bits/stdc++.h> using namespace std; const int N=1e5+10; stack<int>s; ve

【luogu P1144 短路計數】 題解

color style ref bsp vector cst itl () col 題目鏈接:https://www.luogu.org/problemnew/show/P1144 1 #include <iostream> 2 #include &

洛谷 P1144 短路計數 解題報告

++ 報告 CA amp ret 正整數 std 最短路 www P1144 最短路計數 題目描述 給出一個\(N\)個頂點\(M\)條邊的無向無權圖,頂點編號為\(1-N\)。問從頂點1開始,到其他每個點的最短路有幾條。 輸入輸出格式 輸入格式: 第一行包含2個正整數\(

洛谷_P1144_短路計數

gin lis ron 大於 font har bottom 覆蓋 otto 題目大意: 給出一個 N 個頂點 M 條邊的無向無權圖,頂點編號為 1-N。問從頂點 1 開始,到其他每個點的最短路有幾條。 題解: 更新邊長的時候如果大於號就覆蓋,有相同最短路徑就相

【洛谷習題】短路計數

代碼 main tar name tex 並且 set getchar() eof 水題鏈接:https://www.luogu.org/problemnew/show/P1144 這道題就是很水啊,水到我居然不用最短路算法就做了出來。因為每條邊的權值都為1,最

短路計數

class sizeof cst get 更新 math truct turn 操作 題目:   給出一個N個頂點M條邊的無向無權圖,頂點編號為1−N。問從頂點1開始,到其他每個點的最短路有幾條。