牛客网提高五练习

吉老师的题,出的很有水平(应该是T1,T2难度)

同余方程

题意#

给出 l1,l2,r1,r2,m 询问 i=l1r1j=l2r2[m|ij]

分析#

对于前 30 分,我们可以简单枚举

对于第二个点,我们已知一个数

显然对于 1018 的数据范围,我们只能考虑 O(1) 或者 O(logn) 的算法

由于如果需要枚举 [l,r] 中的数,时间复杂度将会乘上 O(n),我们需要考虑直接进行计算

因为确定了 x ,不同 y 的值与 x 异或的值一定不一样,并且一定是紧密排布的

因此我们可以直接计算出这些数的开头以及结尾,还有 m 的倍数的个数

对于所有数据

我们考虑将这个问题转换为前缀问题

f(x,y)i=0xj=0y[m|ij]

那么我们可以将原问题转换为 f(r1,r2)f(l1,r2)f(r1,l2)+f(l1,l2)

f(x,y) 可以通过二进制分解后选取一部分做上一种情况来求解

代码#

Copy
#include <cstdio> #include <iostream> using namespace std; #define qword long long qword l1, l2, r1, r2, p, ans = 0; #define M 998244353LL qword solve(qword l, qword r, qword p) { if (l % p) l = (l / p + 1) * p; if (r % p) r = (r / p) * p; if (l > r) return 0; return (r / p - l / p + 1) % M; } qword solve(qword a, qword b){ qword res = 0; for (qword i = a; i > 0; i -= i & -i) for (qword j = b; j > 0; j -= j & -j){ qword m = min(i & -i, j & -j); qword n = max(i & -i, j & -j); qword x = ((i - (i & -i)) ^ (j - (j & -j))) | (n - 1); qword y = x - n + 1; (res += m % M * solve(y, x, p) % M) %= M; } return res; } int main() { cin >> l1 >> r1 >> l2 >> r2 >> p; if (r1 <= 5000 && r2 <= 5000) { for (qword i = l1; i <= r1; ++ i) for (qword j = l2; j <= r2; ++ j) { qword res = i ^ j; if (res % p == 0) ans ++; if (ans > M) ans -= M; } } else ans = solve(++r1, ++r2) - solve(l1, r2) - solve(r1, l2) + solve(l1, l2); cout << (ans + M) % M << endl; }

旅行

题意#

每条边至少走一次,从 1 出发回到 1

解析#

应为这是一个闭合回路,如果我们将重复走的边进行拆分,那这张图就是个欧拉回路

因此,我们要保证每个点的度都是偶数

考虑添加哪些边使得每个点的度是偶数

由于每加一条边,只会改变两个点的度,并且这个度是可以传递的 最短路 + dp? 时间暴了

发现由于边权全部是 2 的幂次,并且幂次高的一定比所有幂次小的的和大,所以我们只要选取了高幂次的,我们就可以通过选取低幂次来降低总的 ans

由此,我们可以发现我们最终选取的值全部都在这个图的 MST

那么原问题将转变成选取那些边,可以使得这棵树上所有的点的权值全是偶数

简单DFS即可

注意取模

代码#

Copy
#include <cstdio> #include <iostream> using namespace std; #define qword long long const int maxn = 5e5 + 10; int n, m, u[maxn], v[maxn], w[maxn], fa[maxn], d[maxn], ver[maxn << 1], head[maxn << 1], Next[maxn << 1]; long long mi[maxn], tot = 0, ans = 0, edge[maxn << 1]; bool vis[maxn]; #define CH ch = getchar() const int q = 998244353; template <class T> inline void read(T &x) { x = 0; int ch, f = 0; for (CH; ch < '0' || ch > '9'; CH) if (ch == '-') f = 1; for (; ch >= '0' && ch <= '9'; CH) x = (x << 1) + (x << 3) + (ch ^ 48); if (f) x = -x; } int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } inline void addEdge(int x, int y, int z) { ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot; } int dfs(int x) { vis[x] = 1; for (int i = head[x]; i; i = Next[i]) { int y = ver[i], z = edge[i]; if (vis[y]) continue; int isAdd = dfs(y); if (isAdd) d[y] ++, d[x] ++, ans += z, ans %= q; } if (d[x] & 1) return 1; else return 0; } int main() { read(n), read(m); mi[0] = 1; for (int i = 1; i <= m; ++ i) { read(u[i]), read(v[i]); fa[i] = i; d[u[i]] ++, d[v[i]] ++; mi[i] = (mi[i - 1] << 1) % q; ans += mi[i]; ans %= q; } for (int i = 1; i <= m; ++ i) { int uu = u[i], vv = v[i]; // ww -> 2 ^ i int x = find(uu), y = find(vv); if (x != y) addEdge(uu, vv, mi[i]), addEdge(vv, uu, mi[i]), fa[x] = y; } dfs(1); cout << ans << endl; }

串串

题意#

如题所述

解析#

T 是从 S 中删除一些字符得到的,T 一共有 (c+dc)

考虑逆过程,我们在 T 上面插入 ac0bd1 来得到 S

但是直接插入会计算重复,比如 T000,插入一个 04 种位置,但是得 到的都是相同的结果

为了避免计算重复,我们限制所有统计的插入方案,必须对应着 TS 中最 靠前的一次出现,比如在上面的例子中,我们只统计 0 插入到最末尾的情况。

显然在这样的要求下, 0 只能插入到 1 前面或者末尾,1 只能插入到 0 前面或者末尾。因此在一共 c+d+1 个间隔中,除了末尾以外,其他间隔都只能插入一种数字

可以枚举末尾有几个 0 几个 1,然后用插板法统计方案

代码#

Copy
#include <cstdio> #define qword long long const int p = 1e9 + 7; const int maxn = 2010; int ans, C[maxn << 1][maxn], a, b, c, d; int main() { scanf("%d %d %d %d",&a, &b, &c, &d); C[0][0] = 1; for (int i = 1; i <= a + b; ++ i){ C[i][0] = 1; for (int j = 1; j <= i; ++ j) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % p; } if(!c || !d){ printf("%lld\n", C[a + b][a]); return 0; } for (int i = 0; i <= a - c; ++ i) for (int j = 0; j <= b - d; ++ j){ qword ret = C[i + j][j]; (ret *= C[c + d][d]) %= p; int cq0 = a - c - i, cq1 = b - d - j; (ret *= C[cq1 + c - 1][c - 1]) %= p; (ret *= C[cq0 + d - 1][d - 1]) %= p; (ans += ret ) %= p; } printf("%d", ans); }
posted @   AlessandroChen  阅读(257)  评论(1编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
CONTENTS