2022 CCPC Henan Provincial Collegiate Programming Contest 题解

news/2025/3/31 13:03:38/文章来源:https://www.cnblogs.com/Young-Cloud/p/18798230

AE

签到秒了

DL

So Hard 做不了一点

I

模拟还是算了

B

dp 的优化

没有解题思路, 陈的观察加刘的码力直接一发过了

CODE
#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int mod=998244353;
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);void solve() {int n;string s;cin>>s;n=s.length();s=" "+s+s;map<char,int>mp;mp['a']=1;mp['e']=2;mp['h']=3;mp['n']=4;int ans=0;for(int k=1;k<=min(15ll,n);k++){vector<int>dp(2*n+1,0);int m=min(n,k);for(int i=m;i<m+n;i++){int cur=0;int d=1;for(int j=i;j>=max(m,i-15);j--){cur=(cur+mp[s[j]]*d)%mod;d*=31;dp[i]=max(dp[j-1]+cur,dp[i]);}//if(m==5) cout<<i<<": "<<dp[i]<<"\n";}//cout<<dp[m+n-1]<<'\n';ans=max(dp[n+m-1],ans);}cout<<ans<<"\n";}
signed main() {IOS;int t = 1;//cin >> t;while (t--) {solve();}
}

C

线段树

解题思路

先不考虑修改,只考虑对于一个特定的区间,我们如何快速地去回答任意给定的 \(k\)
将一个区间划分成 \(K\) 段就是一个隔板法。但此处我们有限制,对于 \(S_{i + 1} \leq S_i\) 的位置,我们必须插入一个隔板,否则就是不合法的。所以对于一个区间,我们只要知道了该区间有多少个位置必须要插入隔板,我们就可以快速回答任意的 \(K\):假设区间中有 \(N\) 个空位,\(X\) 个必须要插入隔板的位置,则答案就是

\[C_{N - X}^{(K - 1) - X} \]

(只用特判掉一些答案为 0 的情况就好了。)

而且我们发现,对于两个区间的合并也是比较简单的:只用将两个区间的 \(X\) 相加,然后判断下两个区间合并是否会产生新的必须要隔板的位置就好了:

// std::array<int, 3> node 表示一个区间中要维护的信息
// node[0] 代表最左边的字母
// node[2] 代表最右边的字母
// node[1] 代表有多少个必须插入隔板的位置
std::array<int, 3> operator+(const std::array<int, 3>& l, const std::array<int, 3>& r) {return { l[0], l[1] + r[1] + (l[2] >= r[0]), r[2] };
}

接下来只需要解决如何修改,我们就可以用线段树来实现了。

为了方便处理修改,首先把 ABCD 变成 0123,这样修改一次区间中的数 \(x\) 就变成 \((x + 1) \% 4\)

我们只用维护必插位置的数量。而且手玩一下就可以发现,只有使区间端处的 3 变成 0 的修改,才有可能改变必插位置的数量。具体的若最前面的是 3 则修改后少一个,若最后面是 3 则修改后多一个。

于是,上述修改方法配合 lazy tag 我们就可以实现区间修改了:

// cur 是要修改的区间的编号
// c 是修改的次数
void move(int cur, int c) {(tag[cur] += c) %= 4;(tr[cur][0] += c) %= 4;(tr[cur][2] += c) %= 4;// 小于 c 说明有一个从 3 到 0 的过程tr[cur][1] += (tr[cur][2] < c) - (tr[cur][0] < c);
}

对于组合数,预处理以下阶乘和阶乘逆元就好了(main 函数里面)

CODE
constexpr int N = 1e5, Q = 1e5, Inf = 1e9;int a[N];std::array<int, 3> tr[N << 2];
int tag[N << 2];
i64 fac[N + 5], invfac[N + 5];i64 qpow(i64 a, i64 p) {i64 res = 1;while (p) {if (p & 1) {(res *= a) %= Mod;}(a *= a) %= Mod;p >>= 1;}return res;
}
i64 inv(i64 a) {return qpow(a, Mod - 2);
}
i64 C(int n, int m) {return fac[n] * invfac[m] % Mod * invfac[n - m] % Mod;
}std::array<int, 3> operator+(const std::array<int, 3>& l, const std::array<int, 3>& r) {return { l[0], l[1] + r[1] + (l[2] >= r[0]), r[2] };
}void push_up(int cur) {tr[cur] = tr[cur << 1] + tr[cur << 1 | 1];
}void move(int cur, int c) {(tag[cur] += c) %= 4;(tr[cur][0] += c) %= 4;(tr[cur][2] += c) %= 4;tr[cur][1] += (tr[cur][2] < c) - (tr[cur][0] < c);
}void push_down(int cur) {move(cur << 1, tag[cur]);move(cur << 1 | 1, tag[cur]);tag[cur] = 0;return;
}void build(int cur, int l, int r) {if (l + 1 == r) {tr[cur][0] = tr[cur][2] = a[l];tr[cur][1] = 0;return;}int m = l + r >> 1;build(cur << 1, l, m);build(cur << 1 | 1, m, r);push_up(cur);
}
void upd(int cur, int l, int r, int sl, int sr) {if (sl <= l && r <= sr) {move(cur, 1);return;}if (tag[cur]) {push_down(cur);}int m = l + r >> 1;if (m > sl) {upd(cur << 1, l, m, sl, sr);}if (m < sr) {upd(cur << 1 | 1, m, r, sl, sr);}push_up(cur);
}std::array<int, 3> quiry(int cur, int l, int r, int sl, int sr) {if (sl <= l && r <= sr) {return tr[cur];}if (tag[cur]) {push_down(cur);}int m = l + r >> 1;if (m >= sr) {return quiry(cur << 1, l, m, sl, sr);}else if (m <= sl) {return quiry(cur << 1 | 1, m, r, sl, sr);}else {return quiry(cur << 1, l, m, sl, sr) + quiry(cur << 1 | 1, m, r, sl, sr);}
}void solve()
{int n = 0, q = 0;std::string s;std::cin >> n >> q >> s;for (int i = 0; i < n; i++) {a[i] = s[i] - 'A';}build (1, 0, n);while (q--) {int op = 0;int l = 0, r = 0;std::cin >> op >> l >> r;l--;if (op == 1) {upd(1, 0, n, l, r);}else {int k = 0;std::cin >> k;int c = quiry(1, 0, n, l, r)[1];if (k <= c || k > r - l) {std::cout << 0 << '\n';}else {std::cout << C(r - l - 1 - c, k - 1 - c) << '\n';}}}
}int main()
{IOS;int _t = 1;fac[0] = 1;for (int i = 1; i <= N; i++) {fac[i] = 1ll * i * fac[i -  1] % Mod;}invfac[N] = inv(fac[N]);for (int i = N; i >= 1; i--) {invfac[i - 1] = 1ll * i * invfac[i] % Mod;}while (_t--){solve();}sp();return 0;
}

F

构造 观察

解题思路

对于奇数的情况是好处理的,而偶数,最小只能构造出 6 的情况:134,对于大于 6 的偶数在后面添连续的相邻的数就好了。 2 和 4 是真构造不出来。

CODE
void solve()
{int n = 0;std::cin >> n;if (n & 1) {n = n + 1 >> 1;std::cout << n << '\n';for (int i = 1; i <= n; i++) {std::cout << i << ' ';}}else if (n > 4) {n >>= 1;std::cout << n << '\n';n++;std::cout << 1 << ' ';for (int i = 3; i <= n; i++) {std::cout << i << ' ';}}else {std::cout << -1;}std::cout << '\n';return;
}

G

阅读理解题

解题思路

说是阅读理解题是因为只要理解好题意你就会发现什么什么概率都是诈骗。

CODE
void solve()
{int n = 0, m = 0;std::cin >> n >> m;for (int i = 0; i < m; i++) {cnt[i] = 1;}std::string s;for (int i = 0; i < n; i++) {std::cin >> s;for (int j = 0; j < m; j++) {int c = s[j] - '0';if (c == 0) {cnt[j] = 0;}}}int ans = 0;for (int i = 0; i < m; i++) {if (cnt[i]) {ans++;}}std::cout << ans << '\n';
}

H

搜索 暴力

解题思路

按每根管子的形状爆搜就好了。

CODE
bool dfs(int dir, int cx, int cy) {if (cx == 2 && cy == y) {return true;}if (cx < 0 || cx > 1 || cy < 0 || cy >= m) {return false;}if (vis[cx][cy]) {return false;}vis[cx][cy] = true;bool res = false;if (g[cx][cy] == 'I') {res = dfs(dir, cx + dx[dir], cy + dy[dir]);}else {res = dfs((dir + 1) % 4, cx + dx[(dir + 1) % 4], cy + dy[(dir + 1) % 4]);if (not res) {res = dfs((dir + 3) % 4, cx + dx[(dir + 3) % 4], cy + dy[(dir + 3) % 4]);}}vis[cx][cy] = false;return res;
}void solve()
{std::cin >> m >> x >> y;x--, y--;for (int i = 0; i < 2; i++) {for (int j = 0; j < m; j++) {vis[i][j] = false;}}for (int i = 0; i < 2; i++) {std::cin >> g[i];}std::cout << (dfs(2, 0, x) ? "YES" : "NO") << '\n';return;
}

J

MEX 观察

解题思路

写的时候没想清楚写了一坨启发式合并然后 T 了。

直接以权值为 0 的那个点为根往下 dfs 就好了,顺便维护每颗子树的大小和子树中权值最小的是多少。

对于 MEX 为 0 的,就是在于根相邻的子树里找一颗最大的。对于其他 MEX 值 \(x\),先找到权值与该 MEX 值相等的点 \(u\),首先可以判断,MEX 为 \(x\) 的连通块若存在,则必然在点 \(u\) 的上面(因为权值为 0 的点在上面),如何判断存不存在呢?只用看以 \(u\) 为根的子树中节点的最小值是多少就好了,只有当等于 \(x\) 时才存在。

CODE
int n = 0, rt = 0;
int v[N + 5], siz[N + 5], mn[N + 5], ans[N + 5];
std::vector<int> g[N + 5];void dfs(int cur, int fa) {siz[cur] = 1;mn[cur] = v[cur];for (auto &to : g[cur]) {if (to == fa) {continue;}dfs(to, cur);siz[cur] += siz[to];mn[cur] = std::min(mn[cur], mn[to]);}if (mn[cur] == v[cur]) {ans[v[cur]] = n - siz[cur];}
}void solve()
{std::cin >> n;for (int i = 1; i <= n; i++) {std::cin >> v[i];if (v[i] == 0) {rt = i;}}for (int i = 2; i <= n; i++) {int u = 0;std::cin >> u;g[u].push_back(i);g[i].push_back(u);}dfs(rt, 0);int mx = 0;for (auto &to : g[rt]) {mx = std::max(mx, siz[to]);}std::cout << mx << ' ';for (int i = 1; i < n; i++) {std::cout << ans[i] << ' ';}std::cout << n << '\n';return;
}

K

基环树

解题思路

首先根据图建立基环树森林。

最重要的观察就是,在基环树森林中,长度不同的环的个数的数量级是 \(\sqrt{n}\) 的,因为:\(\sum_i^k = {k(k + 1) \over 2}\)

根据这一点,我们将所有长度相同的基环树的信息维护到一起,这样就可以节省大量时间空间。

然后对于基环树上的点,在它到达环之前是一定不断变化的,到达环之后就呈现周期性变化,这个周期只与环长有关(这也是为什么上面将环长相同的树的信息合并到一起)。

然后用个前缀和再特判以下 \(a=b\) 的情况就好了。

CODE
constexpr int N = 1e5, L = 450; // 450 sqrt(2e5)
int to[N + 5], in[N + 5], d[N + 5];
int pre[L][N + 5], len[L], col[N + 5], cnt, idx[N + 5];void dfs(int cur) {if (col[cur] == 0) {dfs(to[cur]);col[cur] =  col[to[cur]]; // 染色d[cur] = d[to[cur]] + 1;pre[col[cur]][d[cur]]++;}return;
}void solve()
{int n = 0;std::cin >> n;for (int i = 1; i <= n; i++) {int f = 0;std::cin >> f;to[i] = f;in[f]++;}std::queue<int> q;for (int i = 1; i <= n; i++) {if (in[i] == 0) {q.push(i);}}while (not q.empty()) {int cur = q.front();q.pop();if (--in[to[cur]] == 0) {q.push(to[cur]);}}// 维护环for (int i = 1; i <= n; i++) {if (in[i] != 0) {// 确定颜色,环长相同的基环树颜色相同int cur = i, l = 0;do {l++;in[cur] = 0;d[cur] = 0;cur = to[cur];} while (cur != i);int c = idx[l];if (c == 0) {c = idx[l] = ++cnt;len[c] = l;}pre[c][0] += l;do{col[cur] = c;cur = to[cur];} while (cur != i);}}for (int i = 1; i <= n; i++) {if (col[i] == 0) {dfs(i);}}for (int i = 1; i <= cnt; i++) {for (int j = 1; j <= n; j++) {pre[i][j] += pre[i][j - 1];}}int qr = 0;std::cin >> qr;while (qr--) {i64 a = 0, b = 0;std::cin >> a >> b;if (a == b) {std::cout << n << '\n';continue;}int ans = 0;for (int l = 1; l <= cnt; l++) {if (a % len[l] == b % len[l]) {ans += pre[l][std::min({ a, b, i64(n) })];}}std::cout << ans << '\n';}return;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/907177.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ctfshow刷题(Updating....)

MISC MISC2(软盘/.img文件) 根据题目可知是软盘文件,添加 .img 后缀挂载到虚拟机即可 MISC50(超级套娃,大胆去做) 打开是png文件,不难发现文件附加,foremost分离后得png加压缩包,对于png文件010查看可得字符串 Sk5DVlM2Mk1NRjVIU1gyTk1GWEgyQ1E9Cg== b64解码发现 纯大写加数字 …

250326 Dualpipe understanding

250326 Dualpipe Understanding 这里的Dualpipe可能作为解决上述Challenges 3中流水线停顿问题的算法层面解决方案。 图4展示了如何重新排列这些组件,调整GPU SM用于通信和计算的比例,使得all-to-all和PP通信都能被隐藏图5展示了DualPipe的完整调度,采用了双向流水线调度,同…

HS6621CM-C是一款集成32 bit CPU、Flash和BLE/2.4G 的多模无线SoC芯片

HS6621CM-C是一款集成32 bit CPU、Flash和Audio的BLE/2.4G 的多模无线SoC芯片,内置64kB SRAM、512kB Flash以及GPIO、SPI、I2C、UART、语音ADC,SAR ADC等多种接口与设备,在单颗芯片上集成了各种2.4GHz物联网标准所需的所有特性和功能, 32pin 5x5 QFN封装; 架构特征如下:内…

NI Multisim14.3软件下载与安装教程

‌Multisim14.3‌是一款由美国国家仪器公司(NI)开发的电路仿真软件,主要用于电子电路的设计、仿真和分析。它适用于工程师、设计师、学生和电子爱好者,帮助他们进行电路设计、测试和验证。 主要功能和应用场景 Multisim14.3具有以下主要功能和应用场景:‌电路仿真‌:支持…

解锁阿里云 ESA 技术,揭秘游戏安全加速与低延时的实现路径

全方位面向游戏行业的安全加速解决方案阿里云 ESA 打造高质量的游戏体验。 如今,游戏行业正处于蓬勃发展与深刻变革的关键时期。根据中国国际数字娱乐产业大会(CDEC)发布的《2024年1-6月中国游戏产业报告》显示2024年上半年国内游戏市场实际销售收入达 1472.67 亿元 ,同比…

全球化运营平台:SAP BTP如何用AI+集成能力破解供应链与合规难题?

在全球经济深度融合的今天,企业如何打破内外业务壁垒,构建高效、智能的全球化运营体系?SAP BTP(业务技术云平台)凭借其一体化技术架构与AI创新能力,正成为企业全球化战略的核心引擎。 SAP BTP:全球化运营的“创新底座” 全球化运营的复杂性不仅在于多地域业务协同,更需…

PostgreSQL技术大讲堂 - 第84讲:重讲PostgreSQL流复制部署

PostgreSQL技术大讲堂 - 第84讲,主题:重讲PostgreSQL流复制部署主讲老师: CUUG数据库老陈,PG中文社区委员、CCF数据库专委会委员 时间:2025年03月29日19:30 地址:钉钉群直播 (群号:89285006175) 欢迎持续关注CUUG PostgreSQL技术大讲堂。

devexpress老版本部署在Linux下gdip问题修复

RUN ln -s /usr/lib/x86_64-linux-gnu/libgdiplus.so.0 /usr/lib/libgdiplus.so

重磅推出稳联技术Profinet转CANopen网关智能工厂解决方案!

重磅推出稳联技术Profinet转CANopen网关智能工厂解决方案! 稳联技术Profinet转CANopen网关应运而生——它如同一座智能桥梁☺,打通两大主流工业协议,让异构网络无缝互联,助您释放设备潜力,实现真正的“万物互联”!为什么选择稳联技术Profinet转CANopen网关? 1、协议转换…

spice环境测试

spice环境测试1、在pve上部署windows操作系统,开机能够正常登录 2、通过ssh登录pve终端中,将虚拟机对应vid配置文件添加以下参数cd /etc/pve/qemu-server 进入pve虚拟机配置文件目录下将对应vid文件添加参数 args: -spice port=61107,addr=0.0.0.0,seamless-migration=on,dis…

apisix~hmac-auth插件的使用

hmac-auth插件需要和 Consumer 一起使用,API 的使用者必须将密匙添加到请求头中以验证其请求,下面介绍它的主要用法 参数algorithm 算法 默认hmac-sha256 ["hmac-sha1", "hmac-sha256", "hmac-sha512"],客户端请求头X-HMAC-ALGORITHM=hmac-sh…