NOIP模拟 17.9.28
公交车
【问题描述】
市内有𝑛个路口和𝑚条双向道路,每条道路都有过路费,第𝑖条道路的过路费
为𝑤%元。市内还有𝑘条公交线路,公交车只在路口停靠,而且一路公交车只会沿
着固定的线路往返行驶。上第𝑖路公交车需要𝑏%元的费用,但费用是一次性的,
即你可以在公交线路上的任意停靠路口下车。但是一旦下车了,如果要再次上车,
则还需要再次付费。
现在你在路口𝑆,你想知道从𝑆出发到其它每个路口的最小费用是多少。
【输入格式】
输入的第一行包含四个整数𝑛、𝑚、𝑘和𝑆,分别代表路口数、道路数、公交
线路数和你所处的路口编号。
接下来的𝑚行,每行包含三个整数𝑥%、𝑦%和𝑘%,代表第𝑖条道路连接𝑥%路口和
𝑦%路口,并且通过这条道路需要缴纳𝑘%元的过路费。
接下来的𝑘行,每行首先是两个整数𝑏%和𝑡%,代表第𝑖路公交车的上车费用为𝑏%
元,而且公交线路共有𝑡%个停靠路口。接下来同一行内有𝑡%个整数,按顺序给出
了这路公交车的各个停靠路口。
【输出格式】
输出一行,包含𝑛个整数,为从路口𝑆到每个路口的最小费用。
【样例】
transprt.in transprt.out
5 4 1 1
1 2 1
2 3 1
3 4 1
4 5 1
2 4 2 3 4 5
0 1 2 3 3
【样例解释】
到路口5 时需要先走到路口2 然后搭公交车,费用为1 + 2 = 3。
其他情况均不需要搭公交车。
【数据规模和约定】
对于30%的数据,𝑛 ≤ 100,𝑘 = 1。
对于另外30%的数据,𝑘 = 0。
对于100%的数据,1 ≤ 𝑛 ≤ 100000,1 ≤ 𝑚 ≤ 200000,1 ≤ 𝑘 ≤ 50000,
Σ𝑡% ≤ 200000, 1 ≤ 𝑘%, 𝑏% ≤ 107。
【题解】
水最短路
公交车虚一个点,从经过的点往虚的点连有边权,虚的点往经过的点连边权0即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 #include <queue> 7 #include <vector> 8 9 inline void read(long long &x) 10 { 11 x = 0;char ch = getchar(), c = ch; 12 while(ch < '0' || ch > '9') c = ch, ch = getchar(); 13 while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar(); 14 if(c == '-')x = -x; 15 } 16 17 const long long INF = 0x3f3f3f3f; 18 const long long MAXN = 300000 + 1000; 19 const long long MAXM = 400000 + 1000; 20 const long long MAXK = 70000 + 1000; 21 const long long MAXT = 400000 + 1000; 22 23 //开longlong 24 25 long long n,m,k,s; 26 27 struct Edge 28 { 29 long long u,v,w,next; 30 Edge(long long _u, long long _v, long long _w, long long _next){u = _u;v =_v;w = _w;next = _next;} 31 Edge(){} 32 } edge[MAXM * 2 + MAXT * 2]; 33 long long head[MAXN + MAXK], cnt; 34 35 inline void insert(long long a, long long b, long long c) 36 { 37 edge[++cnt] = Edge(a,b,c,head[a]); 38 head[a] = cnt; 39 } 40 41 struct Node 42 { 43 long long v,w; 44 Node(long long _v, long long _w){v = _v;w = _w;} 45 Node(){} 46 }; 47 struct cmp 48 { 49 bool operator()(Node a, Node b) 50 { 51 return a.w > b.w; 52 } 53 }; 54 long long d[MAXN + MAXK], b[MAXN + MAXK]; 55 std::priority_queue<Node, std::vector<Node>, cmp> q; 56 57 void dijkstra() 58 { 59 memset(d, 0x3f, sizeof(d)); 60 d[s] = 0; 61 q.push(Node(s, 0)); 62 Node now;long long v, u; 63 while(q.size()) 64 { 65 now = q.top(), q.pop(); 66 if(b[now.v])continue; 67 u = now.v; 68 b[u] = 1; 69 for(register long long pos = head[u];pos;pos = edge[pos].next) 70 { 71 v = edge[pos].v; 72 if(d[v] > d[u] + edge[pos].w) 73 { 74 d[v] = d[u] + edge[pos].w; 75 q.push(Node(v, d[v])); 76 } 77 } 78 } 79 } 80 81 int main() 82 { 83 read(n), read(m), read(k), read(s); 84 long long tmp1, tmp2, tmp3; 85 for(register long long i = 1;i <= m;++ i) 86 { 87 read(tmp1), read(tmp2), read(tmp3); 88 insert(tmp1, tmp2, tmp3); 89 insert(tmp2, tmp1, tmp3); 90 } 91 for(register long long i = 1;i <= k;++ i) 92 { 93 read(tmp3);read(tmp1); 94 for(register long long j = 1;j <= tmp1;++ j) 95 { 96 read(tmp2); 97 insert(n + i, tmp2, 0); 98 insert(tmp2, n + i, tmp3); 99 } 100 } 101 dijkstra(); 102 for(register long long i = 1;i <= n;++ i) 103 printf("%lld ", d[i]); 104 return 0; 105 }
灌溉
【问题描述】
Farmer John 有𝑛个牧场,他希望灌溉他的所有牧场。牧场编号为1 ∼ 𝑛,要
灌溉一个牧场有两种方式,一个是直接在这个牧场建设一个小型水库,另一个是
从别的牧场向这个牧场引水。在第𝑖个牧场建立小型水库需要𝑊%美元,而从第𝑖 个
牧场向第𝑗个牧场引水需要𝑃%,<美元。即便牧场𝑗没有建设小型水库,只要有别的有
水的水库向它引水,那它自己也有水,而且可以向别的水库引水。请告诉FJ 灌
溉所有牧场所需的最小代价。
【输入格式】
输入数据的第一行包含一个整数𝑛,代表牧场的数目。
接下来𝑛行,每行包含一个整数𝑊%,代表建立小型水库的代价。
接下来𝑛行,每行包含𝑛个整数。第𝑖行的第𝑗个数为𝑃%,<。
【输出格式】
输出一行,包含一个整数,即为答案。
【样例】
irrigate.in irrigate.out
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
9
【样例解释】
FJ 应该在第4 块牧场上建立小型水库,然后从这块牧场向其他所有牧场引
水。总费用为3 + 2 + 2 + 2 = 9美元。
【数据规模和约定】
对于20%的数据,𝑛 ≤ 10。
对于40%的数据,𝑛 ≤ 50。
对于60%的数据,𝑛 ≤ 100。
对于100%的数据,1 ≤ 𝑛 ≤ 300,1 ≤ 𝑃%,<,𝑊% ≤ 10>,𝑃%,< = 𝑃<,%,𝑃%,% = 0。
【题解】
水MST
空间开小了,爆掉80
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 7 const long long INF = 0x3f3f3f3f; 8 const long long MAXN = 300000 + 10; 9 const long long MAXM = 1000000 + 1000; 10 11 inline void read(long long &x) 12 { 13 x = 0;char ch = getchar(), c = ch; 14 while(ch < '0' || ch > '9') c = ch, ch = getchar(); 15 while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar(); 16 if(c == '-')x = -x; 17 } 18 19 long long n, ans, rank[MAXN + MAXN], w[MAXM + MAXM], u[MAXM + MAXM], v[MAXM + MAXM], fa[MAXN + MAXN], cnt; 20 21 long long cmp(long long a, long long b) 22 { 23 return w[a] < w[b]; 24 } 25 26 long long find(long long x) 27 { 28 return x == fa[x] ? x : fa[x] = find(fa[x]); 29 } 30 31 int main() 32 { 33 read(n); 34 for(register long long i = n * 10;i >= 1;-- i)fa[i] = i; 35 for(register long long i = 1;i <= n;++ i) 36 ++ cnt, u[cnt] = n + 1, v[cnt] = i, read(w[cnt]), rank[cnt] = cnt; 37 38 long long tmp,f1,f2; 39 for(register long long i = 1;i <= n;++ i) 40 for(register long long j = 1;j <= n;++ j) 41 { 42 read(tmp); 43 if(i >= j)continue; 44 ++cnt, u[cnt] = i, v[cnt] = j, w[cnt] = tmp; 45 rank[cnt] = cnt; 46 } 47 std::sort(rank + 1, rank + 1 + cnt, cmp); 48 for(register long long k = 1;k <= cnt;++ k) 49 { 50 f1 = find(u[rank[k]]), f2 = find(v[rank[k]]); 51 if(f1 == f2)continue; 52 fa[f1] = f2; 53 ans += w[rank[k]]; 54 } 55 printf("%lld", ans); 56 return 0; 57 }
对决
【问题描述】
有两个人在玩一个公平组合游戏。现在我们要分析这两个人谁能获胜。给定
这个游戏的所有局面和局面间的转移,显然它会构成一个DAG。
如果一个局面不能转移到其它局面,那么称这个局面为终止局面;如果一个
局面不能由其它局面转移而来,那么称这个局面为起始局面。
已知这个游戏有𝑛个局面和𝑚条转移(即DAG 有𝑛个点和𝑚条边),有唯一的
起始局面,但可能有多个终止局面。同时,每一个局面都可以由初始局面到达。
现在告诉你所有终止局面是哪方获胜,你能否求出每个局面是先手必胜态还是先
手必败态?
【输入格式】
输入数据的第一行包含两个正整数𝑛和𝑚,分别代表游戏的局面数和转移数。
接下来𝑚行,每行两个整数𝑎%和𝑏%,代表第𝑖条转移是由局面𝑎%转移到局面𝑏%。
接下来一行,有若干个整数,依次代表所有终止局面的获胜方,按照其节点
编号从小到大给出。如果数字为1,那么在该终止局面的先手获胜;如果数字为
0,则后手获胜。
【输出格式】
输出𝑛行,每行包含一个字符串,第𝑖行的字符串代表编号为𝑖的局面的状态。
如果局面为先手必胜态,则输出“First”,否则输出“Last”(均不含引号)。
【样例】
duel.in duel.out
5 5
1 2
1 3
2 4
4 3
4 5
0 1
First
Last
Last
First
First
【样例解释】
局面3 和5 是终止局面,其中3 为后手胜,5 为先手胜。
根据博弈论的有关知识,不难得出答案。
【数据规模和约定】
对于30%的数据,1 ≤ 𝑛,𝑚 ≤ 10。
对于另外30%的数据,𝑚 = 𝑛 − 1。
对于100%的数据,1 ≤ 𝑛 ≤ 100000,1 ≤ 𝑚 ≤ 200000。
【提示】
对于没有接触过博弈论的同学,这里普及一点小常识。如果你之前了解博弈
论相关理论,可以忽略这一段文字。
我们用“局面”代指博弈论中游戏的一个状态。所谓“公平组合游戏”即不
存在随机性、双方信息相同、双方在面临同一局面时的选择也相同的游戏。
比如在本题的游戏中,游戏不存在随机性(游戏进程仅受双方的选择所影响),
双方了解游戏的所有信息(整个游戏的局面和转移),双方在面临同一局面时的
选择也相同(都只能选择从当前局面转移到的其他局面,这些局面被称为后继局
面)。
在一个公平组合游戏中,所有局面都可以分为“先手必胜”和“先手必败”
两种。其含义为,如果双方都以最优策略行动,那么面临这一局面的一方(即先
手) 将必胜或必败。
对于终止局面(即不存在后继局面,当一方到达这一局面时,游戏结束),
其为先手必胜还是先手必败是游戏定义的。在本题中,即为输入给定的。
而对于非终止局面,其为先手必胜还是先手必败是由其后继局面决定的:
• 如果存在一个后继局面为先手必败态,那么当前局面为先手必胜态。
因为先手可以从所有后继局面中任意选择,他当然会选择先手必败的
后继局面——面临这一后继局面的将是后手。
• 否则,即如果所有后继局面都是先手必胜态,那么当前局面为先手必
败态。
根据以上两条规则,可以判定游戏中所有局面的状态。
【题解】
水dfs。题解是top排序,但是容易被卡成有一定常数的kn^q,k < 1,可能会爆掉
我这个算法是稳稳地O(n),(n), Ω(n)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 7 inline void read(int &x) 8 { 9 x = 0;char ch = getchar(), c = ch; 10 while(ch < '0' || ch > '9') c = ch, ch = getchar(); 11 while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar(); 12 if(c == '-')x = -x; 13 } 14 15 const int INF = 0x3f3f3f3f; 16 const int MAXN = 1000000 + 10; 17 const int MAXM = 2000000 + 10; 18 19 struct Edge 20 { 21 int u,v,next; 22 Edge(int _u, int _v, int _next){u = _u;v = _v;next = _next;} 23 Edge(){} 24 }edge[MAXM]; 25 26 int chudu[MAXN], head[MAXN], rudu[MAXN], zhuangtai[MAXN], cnt, n, m, b[MAXN]; 27 28 inline void insert(int a, int b) 29 { 30 edge[++cnt] = Edge(a,b,head[a]); 31 head[a] = cnt; 32 } 33 34 //1为必败态,2为必胜态 35 36 void dfs(int u) 37 { 38 int flag = 1; 39 for(register int pos = head[u];pos;pos = edge[pos].next) 40 { 41 int v = edge[pos].v; 42 if(b[v]) 43 { 44 if(zhuangtai[v] == 1) 45 flag = 2; 46 } 47 else 48 { 49 dfs(v); 50 if(zhuangtai[v] == 1) 51 flag = 2; 52 } 53 } 54 zhuangtai[u] = flag; 55 b[u] = 1; 56 } 57 58 int main() 59 { 60 read(n), read(m); 61 int tmp1, tmp2; 62 for(register int i = 1;i <= m;++ i) 63 { 64 read(tmp1), read(tmp2); 65 insert(tmp1, tmp2); 66 ++ chudu[tmp1]; 67 ++ rudu[tmp2]; 68 } 69 for(register int i = 1;i <= n;++ i) 70 if(!chudu[i]) 71 read(zhuangtai[i]), ++ zhuangtai[i], b[i] = 1; 72 for(register int i = 1;i <= n;++ i) 73 if(rudu[i] == 0 && !b[i]) 74 dfs(i); 75 for(register int i = 1;i <= n;++i) 76 { 77 if(zhuangtai[i] == 2) 78 printf("First\n"); 79 else 80 printf("Last\n"); 81 } 82 return 0; 83 }