[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
牛牛先声明了一个n*m的int型数组,即int a[n][m];,这个数组被他声明成一个全局变量,所以数组的初值全为0,接下来他要进行p次赋值运算。
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、题面
树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可用树形象表示。树在计算机领域中也得到广泛应用,如在编译源程序时,可用树表示源源程序的语法结构。又如在数据库系统中,树型结构也是信息的重要组织形式之一。一切具有层次关系的问题都可用树来描述。满二叉树,完全二叉树,排序二叉树。
- 数组0下标不使用
- 节点i的左子节点在位置为(2*i);
- 节点i的右子节点在位置为(2*i+1);
- 节点i的父节点在位置为(i/2);
- 根节点被保存到数组下标为1的位置。
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 }