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即可

  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 }
T1

 

灌溉
【问题描述】
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

 

 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 }
T2

 

 

 

对决
【问题描述】
有两个人在玩一个公平组合游戏。现在我们要分析这两个人谁能获胜。给定
这个游戏的所有局面和局面间的转移,显然它会构成一个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)

 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 }
T3

 

posted @ 2017-09-28 18:52  嘒彼小星  阅读(355)  评论(0编辑  收藏  举报