2021 年百度之星·程序设计大赛 - 初赛一
久违一更,距离上次更新已经半年了。
这次题目竟然不是按照难度排序
A. 迷失 (Hdu 6996)
题目大意
给定一张无向图,边的边权为或,在图上随机游走,问从1号点经过k条边到达n号点,经过的边的异或和为1的概率。
解题思路
第一眼,图上随机游走,循环概率,点数不超过100,高斯消元,不想写,跳了。
做完后面的回来看第二眼,哦,恰好经过k条边,设表示当前第号点,当前边权异或和为,经过条边到达号点且边权异或和为的概率。
忽然看到,空间爆了。注意到总是从转移过来的,于是可以用循环队列压成两维,即表示当前状态,表示上一个状态,空间问题解决了,但时间复杂度即还是爆了时间。
忽然发现给定一张图,转移方程式是确定且线性的,矩阵快速幂优化就可以了。时间复杂度为。
具体而言,矩阵规模是,根据转移方程式构造即可。
下面代码中是根据方程式两边乘以了构造的。表示号点的度。
结果卡常,中途运算从改成,矩阵乘法减少不必要的取模才过了。
神奇的代码
#include <bits/stdc++.h> #include <vector> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int MO = 998244353; const int N = 1e2 + 8; const int M = 2e4 + 8; int head[N], nxt[M], ty[M], cnt[N], to[M]; int inv[N]; int n, m, k, num; inline int qpow(int a, int b) { int ans = 1; while(b) { if(b & 1) { ans = 1ll * ans * a % MO; } a = 1ll * a * a % MO; b = b >> 1; } return ans; } struct matrix { int n, m; int data[N * 2][N * 2]; inline void clear() { for (int i = 0; i <= n; i++) for (int j = 0; j <= m; j++) data[i][j] = 0; } }ff, eye; inline matrix operator *(matrix a, matrix b) { matrix qwq; qwq.n = a.n; qwq.m = b.m; qwq.clear(); LL tmp = 0; for (int i = 1; i <= a.n; i++) for (int j = 1; j <= b.m; j++){ tmp = qwq.data[i][j]; for (int k = 1; k <= a.m; k++){ tmp += ((1ll * a.data[i][k] * b.data[k][j])); if (k % 15 == 0) tmp %= MO; } if (tmp >= MO) tmp %= MO; qwq.data[i][j] = tmp; } return qwq; } inline void add(int u, int v, int w){ num ++; nxt[num] = head[u]; to[num] = v; ty[num] = w; head[u] = num; num ++; nxt[num] = head[v]; to[num] = u; ty[num] = w; head[v] = num; cnt[u] ++; cnt[v] ++; } int main(void) { for(int i = 1; i <= 105; ++ i) inv[i] = qpow(i, MO - 2); int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { read(n); read(m); read(k); num = 0; for(int i = 1; i <= n; ++ i){ head[i] = 0; cnt[i] = 0; } for(int u, v, w, i = 1; i <= m; ++ i){ read(u); read(v); read(w); add(u, v, w); } ff.n = ff.m = 2 * n; ff.clear(); for(int i = 1; i <= n; ++ i) for(int j = head[i]; j; j = nxt[j]){ int v = to[j]; int kin = ty[j]; for(int s = 0; s <= 1; ++ s){ int yuan = i; if (s) yuan += n; int nxt = v; if (s ^ kin) nxt += n; ff.data[yuan][nxt] = inv[cnt[v]]; } } eye.n = eye.m = 2 * n; eye.clear(); for(int i = 1; i <= 2 * n; ++ i) eye.data[i][i] = 1; while(k){ if (k & 1) eye = eye * ff; ff = ff * ff; k >>= 1; } int ans = eye.data[1][n + n] * 1ll * cnt[n] % MO * inv[cnt[1]] % MO; write(ans, '\n'); } return 0; }
B. 愿望幽灵 (Hdu 6997)
题目大意
咕咕咕
解题思路
神奇的代码
咕咕咕
C. 鸽子 (Hdu 6998)
题目大意
台电脑,第台坏了。次操作,每次操作交换第和第台电脑,你可以跳过若干次操作。
问对于每个,最终坏的电脑的位置是第台的最小跳过次数是多少。无法实现输出-1。
解题思路
设表示前次操作后,坏的电脑是第台的最小跳过次数。容易发现每次操作只会更改两个值,即和只有和会发生变化,所以复用数组转移其实是的,总时间复杂度为。
具体而言,转移时考虑是否跳过本次操作,于是,,注意等式右边的值是原来的值,不是本次更新后的值。注意无法实现的情况。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 1e5 + 8; int n, m, k; int dp[N]; int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { read(n); read(m); read(k); for(int i = 1; i <= n; ++ i) dp[i] = -1; dp[k] = 0; for(int u, v, i = 1; i <= m; ++ i){ read(u); read(v); int su = dp[u]; int sv = dp[v]; if (su == -1 && sv == -1) continue; if (sv == -1){ dp[u] = su + 1; dp[v] = su; } else if (su == -1){ dp[v] = sv + 1; dp[u] = sv; } else{ dp[u] = min(su + 1, sv); dp[v] = min(sv + 1, su); } } for(int i = 1; i <= n; ++ i) printf("%d%c", dp[i], i == n ? '\n' : ' '); } return 0; }
D. 萌新 (Hdu 6999)
题目大意
给定两个正整数,求最小和最大的满足,且。
不存在则输出。
解题思路
假设,题意即为即即,所以最小即为的最小质因数,最大即为。
时无解$。
时特判时无解,否则最小为,最大为。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int a, b; read(a); read(b); int dis = max({a,b}) - min({a, b}); if (dis == 1){ puts("-1 -1"); }else if (dis == 0){ if (a == 1){ puts("-1 -1"); } else printf("2 %d\n", a); }else{ int ans = 2; int up = sqrt(dis); while(dis % ans != 0 && ans <= up) ++ ans; if (dis % ans != 0) ans = dis; printf("%d %d\n", ans, dis); } } return 0; }
E. 二分 (Hdu 7000)
题目大意
有个格子,你现在在第个格子,想跳到第个格子。
每次,如果你当前的格子编号小于,你会等概率跳到比当前编号更大的一个格子中,反之则等概率跳到比当前编号更小的一个格子中。
求期望跳的次数。
解题思路
看上去挺有趣的题,结果被A卡常卡得莫得时间思考,咕咕咕。
神奇的代码
咕咕咕
F. 毒瘤数据结构题 (Hdu 7001)
题目大意
一个长为,初始全为的序列,有次操作,每次可以:
-
表示把位置修改为。
-
表示查询,如果将位置修改为,求最大的满足序列位置到上的值均为。注意只是如果,实际并不做修改。
解题思路
上来先写的题
其实这并不毒瘤呀,用并查集维护连通性就可以了。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 5e6 + 8; int f[N], n; int findfa(int x){ return f[x] == x ? x : f[x] = findfa(f[x]); } int main(void) { read(n); for(int i = 1; i <= n; ++ i){ int x, y; read(x); read(y); if (x == 1){ if (f[y] == 0){ f[y] = y; int nxt = findfa(y + 1); if (nxt != 0) f[y] = nxt; int la = findfa(y - 1); if (la != 0) f[la] = f[y]; } }else{ int ans = findfa(1); if (ans == y - 1) ++ ans; int nxt = findfa(y + 1); if (nxt != 0 && ans >= y) ans = nxt; ++ ans; write(ans, '\n'); } } return 0; }
G. 流年烹茶 (Hdu 7002)
题目大意
咕咕咕
解题思路
咕咕咕
神奇的代码
咕咕咕
H. 猎人杀 (Hdu 7003)
题目大意
个人,其中一个是狼人,其余是猎人。
每个人都会有一个想干掉的人的编号列表,是一个的全排列。
首先狼人干掉自己编号列表的第一人,记为。
随后,会干掉自己列表中,从左到右第一个活着的人,这人会成为新的,重复此步骤。
如果期间狼人被干掉了,则猎人胜利。
若某一个猎人被干掉后,场上除了狼人还有另一个猎人活着,则狼人胜利。
给定一个局面,问你最终谁胜。
解题思路
就是个模拟题,不过题意写得晦涩难懂。一开始在疑惑就一晚狼人干掉人吗?不过注意到每个人想干掉的列表是个全排列,那么白天就一定会游戏结束了,狼人不会再干掉第二个人。
看样例时看到有狼人自刀的情况出现,嗯???
神奇的代码
#include <bits/stdc++.h> #include <exception> #include <vector> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 55; bool dead[N]; int cur[N]; int fav[N][N]; int n, lang; int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { read(n); for(int i = 1; i <= n; ++ i){ int a; read(a); if (a == 1) lang = i; } for(int i = 1; i <= n; ++ i) for(int j = 1; j <= n; ++ j){ read(fav[i][j]); } for(int i = 1; i <= n; ++ i){ dead[i] = false; cur[i] = 1; } int cnt = n; int tar = fav[lang][1]; dead[tar] = true; -- cnt; while(cnt > 2 && !dead[lang]){ while(dead[fav[tar][cur[tar]]]) ++ cur[tar]; tar = fav[tar][cur[tar]]; dead[tar] = true; -- cnt; } if (dead[lang]) puts("lieren"); else puts("langren"); } return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/15085013.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2017-07-31 博弈论之Nim游戏
2017-07-31 智力大冲浪(洛谷P1230)