2024牛客多校 4 (概率,带权并查集,构造)
2024牛客多校 4 (概率,带权并查集,构造)
J - Zero
题面:给出整数
? 有概率是 0 或是 1,且概率相等,一段区间
-
这一段区间不包含 0
-
贡献为长度的
次方
求这个字符串
solution:
首先应该考虑如何统计贡献,如果遍历区间,到达了复杂度
于是自然想到统计每一位的贡献,开始想到统计第
若子串为 ????
则
自然想到如何从
所以发现需要计算
定义
细节为底层特殊情况应该需要特殊判断去赋值,然后递推公式就是:
参考代码如下:
// Created by qyy on 2024/9/13.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define endl "\n"
const long long inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
const long long mod = 998244353;
int n, k;
ll dp[N][32];
string s;
ll fast_pow(ll a, ll n){
ll ans = a % mod;
while(n){
if(n & 1){
ans = (ans * a) % mod;
}
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
ll inv(ll x){
return fast_pow(x, mod - 2);
}
ll C[50][50];
ll cal(int beg, int ed){
if(beg > ed){
return 0;
}
int len = ed - beg + 1;
for(int i = 0; i <= len; i++){
for(int j = 0; j <= 30; j++){
dp[i][j] = 0;
}
if(i != 0){
dp[i][0] = 1;
}
}
ll ans = 0;
if(s[beg] == '?'){
dp[1][0] = inv(2);
}else{
dp[1][0] = 1;
}
for(int j = 1; j <= k; j++){
dp[1][j] = dp[1][j - 1];
}
for(int j = 0; j <= k; j++){
for(int i = 2; i <= len; i++){
int pos = beg + i - 1;
ll res = 1;
for(int x = 0; x < j; x++){
ll tmp = C[j][x] * dp[i - 1][x];
res = (res + tmp) % mod;
}
res = (res + dp[i - 1][j]) % mod;
if(s[pos] == '?'){
dp[i][j] = (res * inv(2)) % mod;
}else{
dp[i][j] = res;
}
}
}
for(int i = 1; i <= len; i++){
ans = (ans + dp[i][k]) % mod;
}
return ans;
}
void solve() {
cin >> n >> k;
cin >> s;
for(int i = 1; i <= 40; i++){
C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; j++){
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
s.push_back('0');
int len = s.size();
int beg = 0, ed = 0;
ll ans = 0;
for(int i = 0; i < len; i++){
if(s[i] == '0'){
ed = i - 1;
ans = (ans + cal(beg, ed)) % mod;
beg = i + 1;
}
}
cout << ans % mod << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
//cin >> t;
while (t--) {
solve();
}
return 0;
}
A - LCT
题面:在一个连接成
solution:显然用带权并查集来解决......
回顾 P1196 银河超级英雄,维护并查集中各点对根节点的距离。
P1196细节:
int find_set(int x){
int tmpfa = fa[x];
if(fa[x] != x){
fa[x] = find_set(fa[x]); // 先把当前父亲节点的更新了,再去计算
dp[x] += dp[tmpfa];
}
return fa[x];
}
需要先更新了当前父节点的状态才能推来,有点 $dp$ 的感觉
于是此题可以同样维护距离根节点距离数组,再针对根节点维护一个最大深度数组,于是考虑两个函数各自应该更新什么。
find_set(x)
应该路径压缩中,将当前根节点的答案累计到该点上。
merge_set(x, y)
应该增加 x 根节点的深度与更新 y 所在根节点的答案。
参考代码如下:
// Created by qyy on 2024/9/13.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define endl "\n"
const long long inf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 7;
int n;
int fa[N], cnt[N], dp[N], ans[N];
void dsu_init(){
for(int i = 1; i <= n; i++){
fa[i] = i;
cnt[i] = 1;
dp[i] = 0;
ans[i] = 0;
}
}
int find_set(int x){
int tmpfa = fa[x];
if(fa[x] != x){
fa[x] = find_set(fa[x]);
dp[x] += dp[tmpfa];
}
return fa[x];
}
void merge_set(int x, int y){
int fx = find_set(x), fy = find_set(y);
if(fx != fy){
ans[fy] = max(ans[fy], ans[fx] + dp[y] + 1);
dp[x] += dp[y] + 1; // 链上的加法
fa[fx] = fy;
}
}
void solve() {
cin >> n;
dsu_init();
for(int i = 1; i < n; i++){
int a, b, c;
cin >> a >> b >> c;
merge_set(b, a);
cout << ans[c] << " ";
}
cout << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)