Description
眾所周知,香港記者跑得比誰都快,這其中其實是有秘訣的。
首先他們會跑最短路,但是最短路會有很多條,而且其他記者也知道要跑最短路,香港記者還統計出了每座城市帶黑框眼鏡的人數,如果一個記者跑路的時候城市帶黑框眼鏡人數的序列字典序比另一個記者大,那么這個記者就會被不可描述的力量續走時間,導致他跑得沒字典序小的記者快。
長者日續萬秒日理萬機,想請你告訴他香港記者經過的總路程和城市帶黑框眼鏡人數的序列,方便他找到香港記者,傳授他們一些人生經驗。
方便起見,設起點為 1 終點為 n。
由於續命的多樣性不可描述的力量,各個城市帶黑框眼鏡的人數各不相同。
Input
輸入文件名為 journalist.in。
第一行,兩個整數 n; m 表示有 n 個城市,城市之間有 m 條有向邊。
第二行, n 個數,表示每個城市帶黑框眼鏡的人數 bi:
接下來 m 行,每行 3 個非負整數 ui; vi; wi 表示一條有向邊的起點,終點,路程。
Output
輸出文件名為 journalist.out。
第一行,一個非負整數表示香港記者經過的總路程。
第二行,若干個非負整數表示香港記者經過的城市帶黑框眼鏡人數的序列。
Sample Input
8 9
1 2 3 4 5 6 7 8
1 2 2
2 3 3
3 8 3
1 4 3
4 5 2
5 8 1
1 6 1
6 7 2
7 8 3
Sample Output
6
1 4 5 8
Hint
對於前 30% 的數據, 2 ≤ n ≤ 2 × 103, 1 ≤ m ≤ 4 × 103。
對於前 60% 的數據,保證數據隨機。
對於另外 30% 的數據,保證所有起點到終點的簡單路徑(沒有環的路徑)長度相同。
對於 100% 的數據, 2 ≤ n ≤ 2 × 105, 1 ≤ m ≤ 4 × 105, 1 ≤ w ≤ 1 × 109,存在至少一條從
起點到終點的最短路。
題解
解法一:
我們可以先求出最短路。
然后搜索求出路徑。
對於$u->v$,如果
$$dist[v]==dist[u]+w(u,v)$$
就繼續拓展。
我們可以加邊的時候先將后繼節點按點權排序,這樣貪心保證搜出的第一條策略就是滿足條件的。

1 #include<set>
2 #include<map>
3 #include<cmath>
4 #include<ctime>
5 #include<queue>
6 #include<stack>
7 #include<vector>
8 #include<cstdio>
9 #include<string>
10 #include<cstdlib>
11 #include<cstring>
12 #include<iostream>
13 #include<algorithm>
14 #define LL long long
15 #define RE register
16 #define IL inline
17 using namespace std; 18 const int N=2e5; 19 const int M=4e5; 20
21 IL int Read() 22 { 23 int sum=0; 24 char c=getchar(); 25 while (c<'0'||c>'9') c=getchar(); 26 while (c>='0'&&c<='9') sum=sum*10+c-'0',c=getchar(); 27 return sum; 28 } 29
30 int n,m,u,v,w; 31 int b[N+5]; 32 struct ss 33 { 34 int u,v,w; 35 }lin[M+5]; 36 bool comp(const ss &x,const ss &y) {return x.u==y.u ? b[x.v]>b[y.v]:x.u<y.u;} 37 struct tt 38 { 39 int to,next,cost; 40 }edge[M+5]; 41 int path[N+5],top; 42 IL void Add(int u,int v,int w); 43
44 LL dist[N+5]; 45 IL void SPFA(); 46
47 int keep[N+5],tot; 48 void put_road(int r); 49
50 int main() 51 { 52 n=Read(),m=Read(); 53 for (RE int i=1;i<=n;i++) b[i]=Read(); 54 for (RE int i=1;i<=m;i++) lin[i].u=Read(),lin[i].v=Read(),lin[i].w=Read(); 55 sort(lin+1,lin+1+n,comp); 56 for (RE int i=1;i<=m;i++) Add(lin[i].u,lin[i].v,lin[i].w); 57 SPFA(); 58 printf("%lld\n",dist[n]); 59 keep[++tot]=b[1]; 60 put_road(1); 61 return 0; 62 } 63
64 IL void Add(int u,int v,int w) 65 { 66 edge[++top].to=v; 67 edge[top].next=path[u]; 68 edge[top].cost=w; 69 path[u]=top; 70 } 71 IL void SPFA() 72 { 73 memset(dist,127/3,sizeof(dist)); 74 dist[1]=0; 75 bool vis[N+5]={0}; 76 vis[1]=1; 77 queue<int>Q; 78 while (!Q.empty()) Q.pop(); 79 Q.push(1); 80 while (!Q.empty()) 81 { 82 int u=Q.front(),v;Q.pop();vis[u]=0; 83 for (RE int i=path[u];i;i=edge[i].next) 84 { 85 v=edge[i].to; 86 if (dist[v]>dist[u]+edge[i].cost) 87 { 88 dist[v]=dist[u]+edge[i].cost; 89 if (!vis[v]) 90 { 91 vis[v]=1; 92 Q.push(v); 93 } 94 } 95 } 96 } 97 return; 98 } 99
100 void put_road(int r) 101 { 102 if (r==n) 103 { 104 for (RE int i=1;i<=tot;i++) printf("%d ",keep[i]); 105 exit(0); 106 } 107 for (int i=path[r];i;i=edge[i].next) if (dist[edge[i].to]==dist[r]+edge[i].cost) 108 { 109 keep[++tot]=b[edge[i].to]; 110 put_road(edge[i].to); 111 tot--; 112 } 113 }
解法二:
我們可以存儲逆邊,逆向做一次$SPFA$。
$pre[u]$表示$u$的后繼節點
松弛的時候如果
$$dist[v]>dist[u]+w(u,v)$$
就
$$dist[v]=dist[u]+w(u,v);$$
如果
$$dist[v]==dist[u]+w(u,v)$$
此時若$pre[v]$的點權大於$u$的點權,我們將
$$pre[v]=u;$$
由於字典序高位越小越好,滿足最優子結構,貪心是可行的。

1 #include<set>
2 #include<map>
3 #include<cmath>
4 #include<ctime>
5 #include<queue>
6 #include<stack>
7 #include<vector>
8 #include<cstdio>
9 #include<string>
10 #include<cstdlib>
11 #include<cstring>
12 #include<iostream>
13 #include<algorithm>
14 #define LL long long
15 #define RE register
16 #define IL inline
17 using namespace std; 18 const int N=2e6; 19 const int INF=~0u>>1; 20
21 IL int Read() 22 { 23 int sum=0; 24 char c=getchar(); 25 while (c<'0'||c>'9') c=getchar(); 26 while (c>='0'&&c<='9') sum=sum*10+c-'0',c=getchar(); 27 return sum; 28 } 29 IL void put(int d) 30 { 31 if (!d) return; 32 put(d/10); 33 putchar(d%10+'0'); 34 } 35
36 IL int Min(const int &a,const int &b) {return a<b ? a:b;} 37 IL int Max(const int &a,const int &b) {return a>b ? a:b;} 38
39 int t,n,k,f,maxn; 40 int a[N+5]; 41 int s[N+5]; 42
43 int main() 44 { 45 t=Read(); 46 while (t--) 47 { 48 n=Read();k=Read();f=Read(); 49 maxn=0; 50 memset(s,0,sizeof(s)); 51 for (RE int i=1;i<=n;i++) a[i]=Read(),maxn=Max(maxn,a[i]),s[a[i]]++; 52 for (RE int i=1;i<=maxn;i++) s[i]+=s[i-1]; 53 for(RE int d=1;d<=maxn;d++) 54 { 55 int cnt=s[d-1]; 56 for(RE int j=d;cnt<=f&&j+k+1<=maxn;j+=d) 57 if (j+k<Min(maxn,j+d-1)) cnt+=s[Min(maxn,j+d-1)]-s[j+k]; 58 if (cnt<=f) put(d),putchar(' '); 59 } 60 putchar('\n'); 61 } 62 return 0; 63 }