2024杭电多校第一场
1005 博弈(hdu7437)#
好吧赛时根本没写到它()
开始看题以为得一步一步算(是什么让我有这么离谱的思路.jpg),看了官方题解才发现自己的愚蠢呜呜,就是说有没有一种可能,A和B前 \(\lfloor n/2 \rfloor\) 位的字符串情况是“等价”的?可以视为把A的字符直接换给B,两方取到每种结果的概率实际上一样。。。 但对于A而言,平局并不计入胜利情况,单独考虑平局即可。
于是开始快乐分类讨论如下:
当 \(n\) 为偶数时,设平局概率为 \(p\) ,A获胜的概率即为 \((1-p)/2\) ; \(n\) 为奇数时,由于A比B多取一个字符,若前 \((n-1)/2\) 个字符相同,则A必胜,设前 \((n-1)/2\) 个字符相同的概率为 \(p\) ,A获胜的概率为 \((1+p)/2\) .
至于如何计算平局的概率,本菜鸡的求法极其抽象()将情况总数看作“长为 \(2n\) 的序列,每个位置在原字符集里挑选一种字符的方案数”,其中前 \((n-\lfloor n/2 \rfloor)\) 位属于A,后 \(\lfloor n/2 \rfloor\) 属于B,这样就避免了重复情况,可以用高中的排列组合知识 \(O(1)\) 计算;用cnt数组记录每个字母出现的次数,前 \(\lfloor n/2 \rfloor\) 个字符相同的概率可同理看作“长为 \(\lfloor n/2 \rfloor\) 的序列,在字符集里挑选的方案数”,其中第 \(i\) 种字符只有 \(cnt[i]/2\) 个,仍然可以 \(O(1)\) 求出。此处注意 \(n\) 的奇偶情况略有不同,特判一下就好,问题不大。
主要代码如下:(还是好长一坨,QwQ)
int n; scanf("%d", &n); int s = 0; for(int i = 1; i <= n; i++) { char c = getchar(); while(c < 'a' || c > 'z') c = getchar(); int h; scanf("%d", &h); s += h; cnt[c - 'a'] += h; } if(s & 1) { int ok = 1; for(int i = 0; i < 26; i++) { if(cnt[i] & 1) { if(ok) { ok = 0; } else { ok = 1; break; } } } if(ok) { printf("%lld\n", inv(2)); continue; } ll x = 1, y = 1, ys = s / 2; for(int i = 0; i < 26; i++) { if(!cnt[i]) continue; x = x * c(s, cnt[i]) % mo; s -= cnt[i]; } for(int i = 0; i < 26; i++) { if(cnt[i] <= 1) continue; cnt[i] /= 2; y = y * c(ys, cnt[i]) % mo; ys -= cnt[i]; } ll p = y * inv(x) % mo; printf("%lld\n", (1 + p) * inv(2) % mo); } else { int ok = 0; for(int i = 0; i < 26; i++) { if(cnt[i] & 1) { ok = 1; break; } } if(ok) { printf("%lld\n", inv(2)); continue; } ll x = 1, y = 1, ys = s / 2; for(int i = 0; i < 26; i++) { if(!cnt[i]) continue; x = x * c(ys * 2, cnt[i]) % mo; y = y * c(ys, cnt[i] / 2) % mo; ys -= cnt[i] / 2; } ll p = y * inv(x) % mo; printf("%lld\n", (1 + mo - p) * inv(2) % mo); }
1006 序列立方 (hdu7438)#
一言以蔽之:人类智慧题(bushi
需要求出序列中每个非空子序列出现次数的立方值的和(什么绕口令),这个立方值看上去十分抽象也不好处理,于是换一种思路:在序列中可重复地取三个子序列,它们相同的情况总数。
考虑动态规划,\(dp[i][j][k]\) 表示已经选了三个相同的子序列,其结尾分别在第 \(i,j,k\) 位,故 \(a[i]=a[j]=a[k]\) 时满足条件。使用前缀和数组 \(s[i][j][k]\) 表示以 \(i,j,k\) 位结尾的方案总数,有 \(a[i]=a[j]=a[k]\) 时 \(dp[i][j][k]=s[i-1][j-1][k-1]\) ,前缀和可用容斥思想维护。
代码如下,注意代码中用ans统计答案,可略去dp数组。
for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for(int i = 0; i <= n; i++) { for(int j = 0; j <= n; j++) { s[i][j][0] = s[i][0][j] = s[0][i][j] = 1; } } ll ans = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { for(int k = 1; k <= n; k++) { ll ss = (s[i - 1][j][k] + s[i][j - 1][k] + s[i][j][k - 1]) % mo; ss -= (s[i - 1][j - 1][k] + s[i - 1][j][k - 1] + s[i][j - 1][k - 1]) % mo; ss = (ss + mo) % mo; ss += s[i - 1][j - 1][k - 1]; ss %= mo; if(a[i] == a[j] && a[i] == a[k]) { ans += s[i - 1][j - 1][k - 1]; ans %= mo; ss += s[i - 1][j - 1][k - 1]; ss %= mo; } s[i][j][k] = ss; } } } printf("%lld\n", ans);
1012 并 (hdu7444)#
赛时死活过不了,赛后全删掉重写了一遍就过了,真好,有一只狸花猫释怀地似了。
看上去非常有扫描线风味的题,其实并没有那么麻烦,由于 \(n \le 2000\) ,\(n^2\) 做法完全能过,我自己就写的差分,赛后听xht说也可以用类似扫描线的存边方式维护+统计。至于期望部分就不难了(我没挂在期望上那就是不难),前面的差分or扫描线统计每块面积被覆盖的次数,设 \(cov[i]\) 表示恰好被 \(i\) 个矩形所覆盖的总面积,选取 \(k\) 个矩形时,期望等于概率(1-没取到该面积的不合法情况数/总情况数,类似容斥思想)乘以面积,总和即 \(\sum\limits_{i=1}^{n}(\binom{n}{k}-\binom{n-i}{k})*cov[i]/\binom{n}{k}\).
以下为差分代码
int n; scanf("%d", &n); c[0][0] = 1; for(int i = 1; i <= n; i++) { c[i][0] = 1; for(int j = 1; j <= i; j++) { c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; if(c[i][j] > mo) c[i][j] -= mo; } } for(int i = 1; i <= n; i++) { int x0, y0, xx, yy; scanf("%d%d%d%d", &x0, &y0, &xx, &yy); x[i * 2 - 1] = x0; x[i * 2] = xx; y[i * 2 - 1] = y0; y[i * 2] = yy; sq[i] = {x0, y0, xx, yy}; } sort(x + 1, x + n * 2 + 1); int nx = unique(x + 1, x + n * 2 + 1) - x - 1; sort(y + 1, y + n * 2 + 1); int ny = unique(y + 1, y + n * 2 + 1) - y - 1; for(int i = 1; i <= n; i++) { sq[i].x0 = lower_bound(x + 1, x + nx + 1, sq[i].x0) - x; sq[i].x = lower_bound(x + 1, x + nx + 1, sq[i].x) - x; sq[i].y0 = lower_bound(y + 1, y + ny + 1, sq[i].y0) - y; sq[i].y = lower_bound(y + 1, y + ny + 1, sq[i].y) - y; d[sq[i].x0][sq[i].y0]++; d[sq[i].x0][sq[i].y]--; d[sq[i].x][sq[i].y0]--; d[sq[i].x][sq[i].y]++; } for(int i = 1; i < nx; i++) { for(int j = 1; j < ny; j++) { ss[j] = ss[j - 1] + d[i][j]; d[i][j] += d[i - 1][j] + ss[j - 1]; s[d[i][j]] += (x[i + 1] - x[i]) * (y[j + 1] - y[j]) % mo; s[d[i][j]] %= mo; } } for(int k = 1; k <= n; k++) { ll ans = 0; for(int i = 1; i <= n; i++) { ans += s[i] * (c[n][k] - c[n - i][k] + mo) % mo; ans %= mo; } ans = ans * inv(c[n][k]) % mo; printf("%lld\n", ans); }
以下为扫描线代码,xht赛时非常顺利地过了,不像我)<-但我认为问题出在我而不是差分上
int n; scanf("%d", &n); c[0][0] = 1; for(int i = 1; i <= n; i++) { c[i][0] = 1; for(int j = 1; j <= i; j++) { c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; if(c[i][j] > mo) c[i][j] -= mo; } } for(int i = 1; i <= n; i++) { int x0, y0, xx, yy; scanf("%d%d%d%d", &x0, &y0, &xx, &yy); x[i * 2 - 1] = x0; x[i * 2] = xx; a[i * 2 - 1] = {x0, xx, y0, 1}; a[i * 2] = {x0, xx, yy, -1}; } n *= 2; sort(x + 1, x + n + 1); int sz = unique(x + 1, x + n + 1) - x - 1; for(int i = 1; i <= n; i++) { a[i].l = lower_bound(x + 1, x + sz + 1, a[i].l) - x; a[i].r = lower_bound(x + 1, x + sz + 1, a[i].r) - x; } sort(a + 1, a + n + 1, cmp); for(int i = 1; i < n; i++) { for(int j = a[i].l; j < a[i].r; j++) { cov[j] += a[i].k; } for(int j = 1; j < sz; j++) { ans[cov[j]] += (x[j + 1] - x[j]) * (a[i + 1].h - a[i].h) % mo; ans[cov[j]] %= mo; } } n /= 2; for(int k = 1; k <= n; k++) { ll s = 0; for(int i = 1; i <= n; i++) { s += ans[i] * (c[n][k] - c[n - i][k] + mo) % mo; s %= mo; } s = s * inv(c[n][k]) % mo; printf("%lld\n", s); }
作者:Aderose_yr
出处:https://www.cnblogs.com/meowqwq/p/18324320
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
喵喵喵)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现