[Nowcoder]2020牛客寒假算法基础集训营3

20200208 第三场

进度(9 / 10) 未完成:B

A、牛牛的DRB迷宫I

1、链接

https://ac.nowcoder.com/acm/contest/3004/A

2、题面

牛牛有一个n*m的迷宫,对于迷宫中的每个格子都为'R','D','B'三种类型之一,'R'表示处于当前的格子时只能往右边走'D'表示处于当前的格子时只能往下边走,而'B'表示向右向下均可以走。

我们认为迷宫最左上角的坐标为(1,1),迷宫右下角的坐标为(n,m),除了每个格子有向右移动以及向下移动的限制之外,你也不能够走出迷宫的边界。

牛牛现在想要知道从左上角走到右下角不同种类的走法共有多少种,请你告诉牛牛从(1,1)节点移动到(n,m)节点共有多少种不同的移动序列,请你输出方案数对10^9+7取余数后的结果。

我们认为两个移动序列是不同的,当且仅当移动序列的长度不同,或者在某一步中采取了不同的移动方式。

3、思路

DP。状态转移:f[i][j] = f[i][j - 1] (if a[i][j - 1] = 'R' / 'B') + f[i - 1][j] (if a[i - 1][j] = 'D' / 'B')。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MAXN 55
 5 #define MOD 1000000007
 6 
 7 int n, m;
 8 char a[MAXN][MAXN];
 9 int f[MAXN][MAXN];
10 
11 int main() {
12     cin >> n >> m;
13     for (int i = 1; i <= n; i++) cin >> a[i] + 1;
14     f[1][1] = 1;
15     for (int i = 1; i <= n; i++)
16         for (int j = 1; j <= m; j++) {
17             if (i == j && j == 1) continue;
18             if (a[i][j - 1] == 'R' || a[i][j - 1] == 'B') (f[i][j] += f[i][j - 1]) %= MOD;
19             if (a[i - 1][j] == 'D' || a[i - 1][j] == 'B') (f[i][j] += f[i - 1][j]) %= MOD;
20         }
21     cout << f[n][m];
22     return 0;
23 } 

C、牛牛的数组越位

1、链接

https://ac.nowcoder.com/acm/contest/3004/C

2、题面

牛牛写了这样一个C/C++程序:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[5][5];
    a[-1][8]=12345;
    printf("%d %d",a[1][-2],a[0][3]);
    return 0;
}
他发现程序可以正常运行,并且输出为:
12345 12345
在C/C++中,这种数组越位行为是Undefined Behaviour(UB)的,也就是程序未定义行为,但是在牛牛的编译器呢,对于这种情况,二维数组的内存是连续的。
也就是说对于二维数组int a[5][5],在牛牛的电脑中是按照a[0][0],a[0][1],a[0][2],a[0][3],a[0][4],a[1][0]...a[1][4],a[2][0]...a[2][4],a[3][0]...a[4][4]
这样储存的。
牛牛所使用的编译器在寻址时是这样处理的,对于一个二维数组a[n][m],若对a[x][y]这个数组元素进行操作,最终访问到的地址为:a的首地址+m*x+y。
所以在寻址时,无论是a[-1][8]还是a[1][-2]最终在牛牛的电脑上使用该编译器编译出的程序均未发生非法访问的行为,他们最终都表示a[0][3]这个数组元素。

本题有T组测试数据。

牛牛先声明了一个n*m的int型数组,即int a[n][m];,这个数组被他声明成一个全局变量,所以数组的初值全为0,接下来他要进行p次赋值运算。

每次他都选择一个二维下标x,y,并且把a[x][y]赋值成val。
当然,牛牛不会老老实实按照C语言的规范来写代码。

如果这p次赋值操作中不存在数组越位行为,则先输出n行,每行m个数。表示操作后的二维数组,数字之间用一个空格隔开,行末没有多余空格。然后输出一行"Accepted"(不含引号),表示正常运行。

如果这p次赋值操作中虽然存在数组越位行为但是没有发生非法访问,则先输出n行,每行m个数。表示操作后的二维数组,数字之间用一个空格隔开,行末没有多余空格。然后输出一行"Undefined Behaviour"(不含引号),表示虽然能够运行,但是牛牛的代码不规范存在隐患。

如果这p次赋值操作中出现了至少一次数组越位并且导致非法访问的行为,请输出一行一个字符串"Runtime error"(不含引号)。

3、思路

根据题意,a[x][y]可以化为a[(m * x + y) / m][(m * x + y) % m],再依次代入,判断是否出现越位以及非法访问的情况。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MAXN 1005
 5 
 6 int n, m, t, p, x, y, v;
 7 int a[MAXN][MAXN];
 8 
 9 int main() {
10     cin >> t;
11     for (int i = 1; i <= t; i++) {
12         int tp = 1;
13         memset(a, 0, sizeof(a));
14         cin >> n >> m >> p;
15         for (int j = 1; j <= p; j++) {
16             cin >> x >> y >> v;
17             int o = m * x + y;
18             if (o >= n * m || o < 0) tp = 3;
19             else {
20                 if (x < 0 || y < 0 || x >= n || y >= m) tp = 2;
21                 a[o / m][o % m] = v;
22             }
23         }
24         if (tp == 3) cout << "Runtime error" << endl;
25         else {
26             for (int j = 0; j < n; j++) {
27                 for (int k = 0; k < m; k++) cout << a[j][k] << ' ';
28                 cout << endl;
29             }
30             cout << ((tp == 2) ? "Undefined Behaviour" : "Accepted") << endl;
31         }        
32     }
33     return 0;
34 }

D、牛牛与二叉树的数组存储

1、链接

https://ac.nowcoder.com/acm/contest/3004/D

2、题面

树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可用树形象表示。树在计算机领域中也得到广泛应用,如在编译源程序时,可用树表示源源程序的语法结构。又如在数据库系统中,树型结构也是信息的重要组织形式之一。一切具有层次关系的问题都可用树来描述。满二叉树,完全二叉树,排序二叉树。

当一颗二叉树是满二叉树时,可以用如下的规则储存:
  1. 数组0下标不使用 
  2. 节点i的左子节点在位置为(2*i); 
  3. 节点i的右子节点在位置为(2*i+1); 
  4. 节点i的父节点在位置为(i/2);
  5. 根节点被保存到数组下标为1的位置。
 

上图是一颗满二叉树,对于每个节点i,i*2是它的左子树,i*2+1是它的右子树,i/2则是它的父亲节点。
当然了,当树不为满二叉树的时候其实也能储存。


上图是树不为满二叉树的情况,我们先用一些虚点将其补充成一颗满二叉树。

根节点还是被储存到数组的第1个位置。

然后对于下标为i的节点,他的左孩子的下标为i*2,它的右孩子的下标为i*2+1,它的父亲节点的下标为i/2。
 
给你一个长度为n的数组,该数组储存了一颗二叉树,数组中仅含有-1和正整数,且整个数组中的正整数是从1到树尺寸连续的,不会出现如1,2,3,5,6,这种缺少4断掉的情况。

请你告诉我树的尺寸和根节点,并按照顺序打印每个节点的父亲节点、左孩子、右孩子分别是谁?

3、思路

线段树做多了对二叉树的构建还是很了解的。根据题意,易得位于数组的第 i 位的节点,父节点位于 i / 2,左右子节点位于 i * 2 和 i * 2 + 1。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MAXN 200005
 5 
 6 int n, a[MAXN], b[MAXN], mx;
 7 
 8 int main() {
 9     cin >> n;
10     memset(a, -1, sizeof(a));
11     for (int i = 1; i <= n; i++) cin >> a[i], b[a[i]] = i, mx = max(mx, a[i]);
12     cout << "The size of the tree is " << mx << endl;
13     cout << "Node " << a[1] << " is the root node of the tree" << endl;
14     for (int i = 1; i <= mx; i++) {
15         int o = b[i];
16         cout << "The father of node " << i << " is " << a[o >> 1]
17         << ", the left child is " << a[o << 1]
18         << ", and the right child is " << a[o << 1 | 1] << endl;
19     }
20     return 0;
21 }

E、牛牛的随机数

1、链接

https://ac.nowcoder.com/acm/contest/3004/E

2、题面

牛牛和牛可乐是一对好朋友,现在牛牛从值域[l1,r1]中随机给出一个数字a,牛可乐从值域[l2,r2]中随机给出一个数字b。问你a⊕b的数学期望。其中⊕为位运算符,表示按位取异或。

为了避免你输出的答案出现精度误差,请你输出一个分数P∗Q^(−1)(mod  10^9+7) ,其中Q^(−1)表示在mod条件下的乘法逆元。数据保证gcd(Q,10^9+7)=1,也就是说保证Q^(−1)在该模条件下有意义,并且保证(r1−l1+1)(r2−l2+1)不是mod的倍数。

3、思路

目前花了最多时间做的题目,没有之一。一方面是我思路太不清晰了,一方面被题解绕了个弯,题解说是数位DP,我觉得还是没有体现出DP的特性。而后我的方法运行时间也明显比标程短,标程在我电脑上好像是跑不过的。

首先这个乘法逆元这一段没太懂(关于费马小定理和扩展欧几里得定理尚待补充),但后来事实证明就是(P * Q ^ 1e9+5) mod 1e9+7 即可。

接着就要考虑如何求这个异或和了。数据量高达1e18,又因为是求异或,所以必然是要用到二进制来计算。根据异或性质,我们发现,存在a∈[l1, r1], b∈[l2, r2],当且仅当他们的二进制形式的第 k 位不同,可以对最终答案提供 2 ^ k 的贡献。也就是说,我们只需要对这两个区间的数分别求出二进制每一位的0和1的个数,最后一起处理,就很轻松得到P和Q了。

根据二进制数的性质(或者通过打表发现),每一位的0和1的个数是有一定规律的。从区间内不是全部为0或1的最高位开始处理,设当前为第 k 位,因为0和1是相间分布,并且每一段个数分别为2 ^ k个,我们只需找出 l 和 r 所在的段是0还是1,求出所在的0/1段在区间内有多少个0/1,再求出区间内有多少个完整的0/1段,相加即可。

由于之前把全部为0或1的位无视了,最后再加上即可。

这种处理方式基本就是模拟了,但是因为思路比较麻烦,代码实际写起来很容易出bug。

最后,切记对于本来就接近long long的数的计算时,一定对于每一步相加相乘都要取模数,同时如果还存在减法,还要考虑最后可能是负数的情况,要加成正数并再次取模。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MOD 1000000007
 5 typedef long long ll;
 6 
 7 ll l[2], r[2], t, h, f[65][2][2], a, b, mx;
 8 
 9 ll qpow(ll x, ll y) {
10     ll res = 1, b = x;
11     while (y) {
12         if (y & 1) (res *= b) %= MOD;
13         (b *= b) %= MOD;
14         y >>= 1;
15     }
16     return res;
17 }
18 
19 ll getMax(ll o) {
20     for (ll i = 62; i >= 0; i--) {
21         ll oo = 1ll << i;
22         if (oo & o) return i;
23     }
24 }
25 
26 void work(ll o) {
27     ll nl = l[o], nr = r[o], xo = nl ^ nr;
28     for (h = 0; (1ll << h) <= xo; h++); 
29     h--; 
30     for (ll i = h; i <= 62; i++) {
31         ll oo = 1ll << i;
32         if (!(xo & oo) && oo & nl) nl -= oo, nr -= oo;
33     }
34     mx = max(max(mx, getMax(l[o])), getMax(r[o]));
35     for (; h >= 0; h--) {
36         ll oo = 1ll << h, pr, pl, f1 = 0, f2 = 0;
37         if (nr & oo) (f[h][o][1] += nr - oo + 1) %= MOD, pr = r[o] - (nr - oo), nr -= oo, f1 = 1; 
38         else (f[h][o][0] += nr + 1) %= MOD, pr = r[o] - nr; 
39         if (nl & oo) (f[h][o][1] += (oo << 1) - nl) %= MOD, pl = l[o] + ((oo << 1) - nl), nl -= oo, f2 = 1;
40         else (f[h][o][0] += oo - nl) %= MOD, pl = l[o] + (oo - nl); 
41         if (pl == pr) continue;
42         if (f1 ^ f2) (f[h][o][0] += (pr - pl) / 2) %= MOD, (f[h][o][1] += (pr - pl) / 2) %= MOD;
43         else (f[h][o][0] += (f1 ? oo : 0) + (pr - pl - oo) / 2) %= MOD, 
44             (f[h][o][1] += (!f1 ? oo : 0) + (pr - pl - oo) / 2) %= MOD;
45     }
46 }
47 
48 int main() {
49     cin >> t;
50     for (int i = 1; i <= t; i++) {
51         memset(f, 0, sizeof(f)), a = 0, mx = 0;
52         cin >> l[0] >> r[0] >> l[1] >> r[1];
53         work(0), work(1);
54         for (ll j = 0; j <= mx; j++) {
55             ll oo = 1ll << j;
56             for (ll k = 0; k <= 1; k++)
57                 if (!f[j][k][1] && !f[j][k][0]) 
58                     if (r[k] & oo) f[j][k][1] = (r[k] - l[k] + 1) % MOD;
59                     else f[j][k][0] = (r[k] - l[k] + 1) % MOD;
60         }
61         for (ll j = 0; j <= mx; j++) {
62             ll oo = 1ll << j;
63             (a += ((f[j][0][1] * f[j][1][0] % MOD + f[j][0][0] * f[j][1][1] % MOD) % MOD) * (oo % MOD) % MOD) %= MOD;
64         }
65         a += MOD, a %= MOD;
66         (b = ((r[1] - l[1] + 1) % MOD) * ((r[0] - l[0] + 1) % MOD)) %= MOD;
67         cout << a * qpow(b, MOD - 2) % MOD << endl;
68     }
69     return 0;
70 }

F、牛牛的Link Power I

1、链接

https://ac.nowcoder.com/acm/contest/3004/F

2、题面

牛牛有一颗大小为n的神奇Link-Cut 数组,数组上的每一个节点都有两种状态,一种为link状态,另一种为cut状态。数组上任意一对处于link状态的无序点对(即(u,v)和(v,u)被认为是同一对)会产生dis(u,v)的link能量,dis(u,v)为数组上u到v的距离。

我们定义整个数组的Link能量为所有处于link状态的节点产生的link能量之和。

一开始数组上每个节点的状态将由一个长度大小为n的01串给出,'1'表示Link状态,'0'表示Cut状态。

牛牛想要知道整个数组的Link能量,为了避免这个数字过于庞大,你只用输出答案对10^9+7取余后的结果即可。

3、思路

当时做的时候脑子可能有点抽?推了个很怪的递推式。。

其实很简单,假设01串里有m个1,那么Link-Cut组合个数只和m有关而与1的位置无关。我们假设相邻的1的距离分别为d[1], d[2], ..., d[m - 1],接着可以从m = 2, 3, ...枚举,把所有组合的可能列出来,找到规律就简单了。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MAXN 100005
 5 #define MOD 1000000007
 6 
 7 typedef long long ll;
 8 
 9 ll n, tot, x, ans, b[MAXN];
10 char a[MAXN];
11 
12 int main() {
13     cin >> n >> a + 1;
14     for (int i = 1; i <= n; i++)
15         if (a[i] == '1') {
16             if (x) b[++tot] = i - x;
17             x = i;
18         }
19     for (ll i = 1, o = tot, d = o - 2; i <= (tot + 1) / 2; i++, o += d, d -= 2) 
20         (ans += (b[i] + (i == tot - i + 1 ? 0 : b[tot - i + 1])) * o) %= MOD;
21     cout << ans;
22     
23     return 0;
24 }

G、牛牛的Link Power II

1、链接

https://ac.nowcoder.com/acm/contest/3004/G

2、题面

牛牛有一颗大小为n的神奇Link-Cut 数组,数组上的每一个节点都有两种状态,一种为link状态,另一种为cut状态。数组上任意一对处于link状态的无序点对(即(u,v)和(v,u)被认为是同一对)会产生dis(u,v)的link能量,dis(u,v)为数组上u到v的距离。

我们定义整个数组的Link能量为所有处于link状态的节点产生的link能量之和。

一开始数组上每个节点的状态将由一个长度大小为n的01串给出,'1'表示Link状态,'0'表示Cut状态。

牛牛想要知道一开始,以及每次操作之后整个数组的Link能量,为了避免这个数字过于庞大,你只用输出答案对10^9+7取余后的结果即可。

3、思路

在F题基础上多了个可修改多次询问。先来研究下每增加一个1对原答案有什么影响。

如图所示,假设原来的a[6]从0变成了1,原来的a[3], a[11], a[13]之间的距离不变,增加的是该3个1分别到a[6]的距离,即(6 - 3) + (11 - 6) + (13 - 6) = 15。

那么我们就有个思路:对于每次操作,假设a[i]从0变成1,答案的变化值为所有原为1的位置和i的距离之和,而位于i左侧的距离为i - x,右侧为x - i,所以我们需要统计出左侧和右侧1的个数以及他们的位置之和。

大概想了下就是用线段树维护了,维护1的个数前缀和以及1所在的位置的前缀和,这样每次对于a[i]的从0到1,先求出[1, i)的1的个数a以及位置之和b,再求出(i, N]的1的个数c以及位置之和d,答案就很容易算出来了。复杂度O(m log n)。

对了,如果是从1变成0,则变化值取负即可。

这道题竟然成为目前集训营里我最费心思去调的题目。。还好最后找出那个sb问题了。代码能力还有待提升。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 100005
 5 #define MOD 1000000007
 6 
 7 #define ls o << 1
 8 #define rs o << 1 | 1
 9 
10 typedef long long ll;
11 
12 ll n, m, ans, f, v, b[N], tot, x;
13 char ch[N];
14 
15 struct Tree {
16     ll n, s;
17     Tree() {}
18     Tree(ll _n, ll _s) { n = _n; s = _s; }
19     Tree operator + (const Tree& a) {
20         Tree t;
21         t.n = this -> n + a.n;
22         t.s = this -> s + a.s;
23         return t;
24     } 
25 } t[N << 2];
26 
27 void upd(int o, int l, int r) {
28     if (l == r) {
29         t[o] = f == 1 ? Tree(1, v) : Tree(0, 0);
30         return;
31     }
32     int m = (l + r) >> 1;
33     if (v <= m) upd(ls, l, m);
34     else upd(rs, m + 1, r);
35     t[o] = t[ls] + t[rs];
36 }
37 
38 Tree query(int o, int l, int r) {
39     int m = (l + r) >> 1;
40     if (1 <= l && r <= v) return t[o];
41     return (1 <= m ? query(ls, l, m) : Tree(0, 0)) + (v > m ? query(rs, m + 1, r) : Tree(0, 0));
42 }
43 
44 int main() {
45     cin >> n >> ch + 1;
46     for (int i = 1; i <= n; i++) {
47         if (ch[i] == '1') {
48             f = 1, v = i, upd(1, 1, N);
49             if (x) b[++tot] = i - x;
50             x = i;
51         }
52     }
53     for (ll i = 1, o = tot, d = o - 2; i <= (tot + 1) / 2; i++, o += d, d -= 2) 
54         (ans += (b[i] + (i == tot - i + 1 ? 0 : b[tot - i + 1])) * o) %= MOD;
55     cout << ans << endl;
56     cin >> m;
57     for (int i = 1; i <= m; i++) {
58         cin >> f >> v;
59         if (f == 2) upd(1, 1, N);
60         Tree o = query(1, 1, N);
61         (ans += (f == 1 ? 1 : -1) * (v * (2 * o.n - t[1].n) + t[1].s - 2 * o.s)) %= MOD;
62         if (ans < 0) ans = -ans, ans %= MOD, ans = -ans, ans += MOD;
63         cout << ans << endl;
64         if (f == 1) upd(1, 1, N);    
65     }
66     return 0;
67 } 

H、牛牛的k合因子数

1、链接

https://ac.nowcoder.com/acm/contest/3004/H

2、题面

合数是指自然数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。

牛牛最近在研究“k合因子数”,所谓“k合数”是指一个数的所有因子中,是合数的因子共有k个。
例如20的因子有1,2,4,5,10,20,其中4,10,20为合数,它有3个合数因子,就称20是一个 “3合因子数”
牛牛想要知道1~n中给定k的情况下k合因子数的数目。

3、思路

当时做的时候脑子可能有点抽?*2

刚刚其实也抽了一下(

对于1~n依次枚举其所有因子,判断是否和合数的同时记录因子中合数的个数即可。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MAXN 100005
 5 
 6 int a[MAXN], tot[MAXN], f[MAXN], n, m, o;
 7 
 8 int main() {
 9     cin >> n >> m;
10     for (int i = 1; i <= n; i++) {
11         for (int j = 2; j <= sqrt(i); j++)
12             if (i % j == 0) {
13                 f[i] = 1, a[i] += (f[j] == 1) + (f[i / j] == 1);
14                 if (j == sqrt(i) && f[j] == 1) a[i]--;
15             }
16         tot[a[i] += f[i]]++;
17     }
18     for (int i = 1; i <= m; i++) cin >> o, cout << tot[o] << endl;
19     return 0;
20 }

I、牛牛的汉诺塔

1、链接

https://ac.nowcoder.com/acm/contest/3004/I

2、题面

汉诺塔是一个经典问题,相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置n个金盘。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。

汉诺塔以及其衍生问题往往使用递归来求解,也是学习和理解递归很好的老师。

其伪代码如下
Function Hanoi(n,a,b,c)
    if n==1 then
        print(a+'->'+c)
    else
        Hanoi(n-1,a,c,b)
        print(a+'->'+c)
        Hanoi(n-1,b,a,c)
    end if
end Function 

牛牛很快就理解了代码的意思并且写出了求解汉诺塔的程序,他现在想研究汉诺塔的规律。
请你统计以下信息:A->B,A->C,B->A,B->C,C->A,C->B的次数,以及所有移动的总步数。

3、思路

好小的时候就玩过汉诺塔,当时就发现这就是个规律游戏(

不过我之前并没想过这是可以用递归解决的问题。先把这段伪代码运行出来,显然时间复杂度不满足n <= 60的情况,然后根据打出来的一部分数据来找规律,瞎推了个递推式,然后就全部打表出来了,懒得求公式了。。

4、代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

int n;
const ll ans[6][61] = {
{0, 0, 1, 1, 4, 4, 15, 15, 58, 58, 229, 229, 912, 912, 3643, 3643, 14566, 14566, 58257, 58257, 233020, 233020, 932071, 932071, 3728274, 3728274, 
14913085, 14913085, 59652328, 59652328, 238609299, 238609299, 954437182, 954437182, 3817748713, 3817748713, 15270994836, 15270994836, 
61083979327, 61083979327, 244335917290, 244335917290, 977343669141, 977343669141, 3909374676544, 3909374676544, 15637498706155, 
15637498706155, 62549994824598, 62549994824598, 250199979298369, 250199979298369, 1000799917193452, 1000799917193452, 4003199668773783, 
4003199668773783, 16012798675095106, 16012798675095106, 64051194700380397, 64051194700380397, 256204778801521560},
{0, 1, 1, 3, 3, 9, 9, 31, 31, 117, 117, 459, 459, 1825, 1825, 7287, 7287, 29133, 29133, 116515, 116515, 466041, 466041, 1864143, 1864143, 
7456549, 7456549, 29826171, 29826171, 119304657, 119304657, 477218599, 477218599, 1908874365, 1908874365, 7635497427, 7635497427, 
30541989673, 30541989673, 122167958655, 122167958655, 488671834581, 488671834581, 1954687338283, 1954687338283, 7818749353089, 
7818749353089, 31274997412311, 31274997412311, 125099989649197, 125099989649197, 500399958596739, 500399958596739, 2001599834386905, 
2001599834386905, 8006399337547567, 8006399337547567, 32025597350190213, 32025597350190213, 128102389400760795, 128102389400760795},
{0, 0, 0, 1, 1, 6, 6, 27, 27, 112, 112, 453, 453, 1818, 1818, 7279, 7279, 29124, 29124, 116505, 116505, 466030, 466030, 1864131, 1864131, 
7456536, 7456536, 29826157, 29826157, 119304642, 119304642, 477218583, 477218583, 1908874348, 1908874348, 7635497409, 7635497409, 
30541989654, 30541989654, 122167958635, 122167958635, 488671834560, 488671834560, 1954687338261, 1954687338261, 7818749353066, 
7818749353066, 31274997412287, 31274997412287, 125099989649172, 125099989649172, 500399958596713, 500399958596713, 2001599834386878, 
2001599834386878, 8006399337547539, 8006399337547539, 32025597350190184, 32025597350190184, 128102389400760765, 128102389400760765},
{0, 0, 1, 1, 4, 4, 15, 15, 58, 58, 229, 229, 912, 912, 3643, 3643, 14566, 14566, 58257, 58257, 233020, 233020, 932071, 932071, 3728274, 3728274, 
14913085, 14913085, 59652328, 59652328, 238609299, 238609299, 954437182, 954437182, 3817748713, 3817748713, 15270994836, 15270994836, 
61083979327, 61083979327, 244335917290, 244335917290, 977343669141, 977343669141, 3909374676544, 3909374676544, 15637498706155, 
15637498706155, 62549994824598, 62549994824598, 250199979298369, 250199979298369, 1000799917193452, 1000799917193452, 4003199668773783, 
4003199668773783, 16012798675095106, 16012798675095106, 64051194700380397, 64051194700380397, 256204778801521560},
{0, 0, 0, 0, 2, 2, 12, 12, 54, 54, 224, 224, 906, 906, 3636, 3636, 14558, 14558, 58248, 58248, 233010, 233010, 932060, 932060, 3728262, 3728262, 
14913072, 14913072, 59652314, 59652314, 238609284, 238609284, 954437166, 954437166, 3817748696, 3817748696, 15270994818, 15270994818, 
61083979308, 61083979308, 244335917270, 244335917270, 977343669120, 977343669120, 3909374676522, 3909374676522, 15637498706132, 
15637498706132, 62549994824574, 62549994824574, 250199979298344, 250199979298344, 1000799917193426, 1000799917193426, 4003199668773756, 
4003199668773756, 16012798675095078, 16012798675095078, 64051194700380368, 64051194700380368, 256204778801521530},
{0, 0, 0, 1, 1, 6, 6, 27, 27, 112, 112, 453, 453, 1818, 1818, 7279, 7279, 29124, 29124, 116505, 116505, 466030, 466030, 1864131, 1864131, 
7456536, 7456536, 29826157, 29826157, 119304642, 119304642, 477218583, 477218583, 1908874348, 1908874348, 7635497409, 7635497409, 
30541989654, 30541989654, 122167958635, 122167958635, 488671834560, 488671834560, 1954687338261, 1954687338261, 7818749353066, 
7818749353066, 31274997412287, 31274997412287, 125099989649172, 125099989649172, 500399958596713, 500399958596713, 2001599834386878, 
2001599834386878, 8006399337547539, 8006399337547539, 32025597350190184, 32025597350190184, 128102389400760765, 128102389400760765}
};

int main() {
    cin >> n;
    cout << "A->B:" << ans[0][n] << endl;
    cout << "A->C:" << ans[1][n] << endl;
    cout << "B->A:" << ans[2][n] << endl;
    cout << "B->C:" << ans[3][n] << endl;
    cout << "C->A:" << ans[4][n] << endl;
    cout << "C->B:" << ans[5][n] << endl;
    cout << "SUM:" << ans[1][n] + ans[2][n] + ans[3][n] + ans[4][n] + ans[5][n] + ans[0][n];
    return 0;
}

(非(qi)常(chou)漂(wu)亮(bi)的一份代码)

J、牛牛的宝可梦Go

1、链接

https://ac.nowcoder.com/acm/contest/3004/J

2、题面

牛牛所在的W市是一个不太大的城市,城市有n个路口以及m条公路,这些双向连通的公路长度均为1,保证你可以从一个城市直接或者间接移动到所有的城市。牛牛在玩宝可梦Go,众所周知呢,这个游戏需要到城市的各个地方去抓宝可梦,假设现在牛牛知道了接下来将会刷出k只宝可梦,他还知道每只宝可梦的刷新时刻、地点以及该宝可梦的战斗力,如果在宝可梦刷新时,牛牛恰好在那个路口,他就一定能够抓住那只宝可梦。

由于游戏公司不想让有选择恐惧症的玩家为难,所以他们设计不存在任何一个时刻同时刷出两只及以上的宝可梦。

假设不存在任何一个时刻会同时刷出两只宝可梦,牛牛一开始在城市的1号路口,最开始的时刻为0时刻,牛牛可以在每个时刻之前移动到相邻他所在位置的路口,当然他也可以保持原地不动,他现在想知道他能够捕获的宝可梦战斗力之和最大为多少?

3、思路

瞟了眼题解才意识到是个DP。

f[i]表示时间i内能得到的最大战斗力。因为时间范围 <= 10 ^ 9,而宝可梦个数 <= 10 ^ 5,并且同时不会出现两个宝可梦,故可以直接将宝可梦按照直接排序,f[i]表示第i个宝可梦出现时能得到的最大战斗力。

做的时候被状态转移给困扰了。其实忽视了题中一个很重要的条件,即这个图n <= 200,并且是权值均为1的点,也就是说两点之间距离最大为200,也就是说不管时间前后涉及到多长,转移的时候都只要考虑时间差在200以内的宝可梦就行了,复杂度仅仅只有O(n * k)。

4、代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 #define MAXN 205
 7 #define MAXK 100005
 8 #define INF 0x3f3f3f3f
 9 
10 ll n, m, dis[MAXN][MAXN], u, v, k, mx[MAXN], f[MAXK];
11 
12 struct pkm {
13     ll t, p, w;
14 } a[MAXK];
15 
16 struct cmp {
17     bool operator () (pkm a, pkm b) {
18         return a.t < b.t;
19     }
20 } x;
21 
22 void floyd() {
23     for (int i = 1; i <= n; i++)
24         for (int j = 1; j <= n; j++)
25             for (int k = 1; k <= n; k++)
26                 dis[j][k] = min(dis[j][k], dis[j][i] + dis[i][k]);
27 }
28 
29 int main() {
30     cin >> n >> m;
31     memset(dis, INF, sizeof(dis));
32     for (ll i = 1; i <= n; i++) dis[i][i] = 0;
33     for (ll i = 1; i <= m; i++) cin >> u >> v, dis[u][v] = dis[v][u] = 1;
34     floyd();
35     cin >> k;
36     for (ll i = 1; i <= k; i++) {
37         cin >> a[i].t >> a[i].p >> a[i].w;
38     }
39     sort(a + 1, a + k + 1, x);
40     a[0].p = 1;
41     for (ll i = 1; i <= k; i++) {
42         f[i] = i > n ? mx[i - n] + a[i].w : -INF;
43         for (ll j = 1; j <= min(i, n); j++) {
44             if (a[i - j].t + dis[a[i - j].p][a[i].p] <= a[i].t)
45                 f[i] = max(f[i], f[i - j] + a[i].w);
46         }
47         mx[i] = max(mx[i - 1], f[i]);
48     }
49     cout << mx[k];
50     return 0;
51 } 
原文地址:https://www.cnblogs.com/jinkun113/p/12304793.html