集训4 20250207

集训4 20250207

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ

K:

题目大意:签到

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
void solve(void){
int x,y,z,a,b,c;
cin>>x>>y>>z>>a>>b>>c;
cout<<max(x*a,max(y*b,z*c))<<endl;
return ;
}
int main()
{
int T;
cin>>T;
while (T--)
solve();
return 0;
}

I:

题目大意:搜索子串

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
char s[100010];
long long pre[100010];
void solve(void){
memset(pre,0,sizeof pre);
int n;
long long ans=0;
cin>>n;
for (int i=1;i<=n;i++){
cin>>s[i];
if (s[i]=='u') pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1];
}
for (int i=3;i<=n;i++){
if (s[i]=='u')
if (s[i+1]=='w')
if (s[i+2]=='a')
if (s[i+3]=='w')
if (s[i+4]=='a')
if (s[i+5]=='u')
if (s[i+6]=='w')
if (s[i+7]=='a')
ans+=pre[i-2];
}
cout<<ans<<endl;
}
int main()
{
int T;
cin >> T;
while (T--)
solve();
return 0;
}

一开始用stl卡了 \(7\) 发,换暴力就过

#include<bits/stdc++.h>
using namespace std;
int main(){
int T;
cin >> T;
while(T--){
int n;
cin >> n;
string s;
cin >> s;
s = " " + s;
int f = 0;
int ans = 0;
for(int i = 1; i <= n; i++){
string t = s.substr(i + 1, 8);//向后匹配
if(t == "uwawauwa") ans += f;//累加前面单独的u
f += s[i] == 'u';//递推记录u的个数
}
cout << ans << endl;
}
}

E:

题目大意:给定一个矩阵,计算在 \(X\) 区域内的元素最大总和

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
long long a[1010][1010];
long long dg[2020],udg[2020];
void solve(void){
memset(dg,0,sizeof dg);
memset(udg,0,sizeof udg);
int n,m;
cin>>n>>m;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
cin>>a[i][j];
dg[j-i+n]+=a[i][j];
udg[i+j-1]+=a[i][j];
}
}
long long maxm=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
maxm=max(maxm,dg[j-i+n]+udg[i+j-1]-a[i][j]);
cout<<maxm<<endl;
}
int main()
{
cintie;
int T;
cin >> T;
while (T--)
solve();
return 0;
}

预处理每个元素在不同对角线上的贡献,最后遍历找最大值即可

关同步加速,不然卡死

B:

题目大意:

一个字符串是平衡的,当且仅当字符串中 "01""10" 连续子串的个数相同。

定义字符串 sval(s) 为满足以下条件的位置数量:

  • 若当前位置上的字符为 '0',将其反置为 '1' 后,字符串 s 是平衡的;
  • 若当前位置上的字符为 '1',将其反置为 '0' 后,字符串 s 是平衡的。

给定一个长度为 n 的字符串 s,仅由字符 '0''1''?' 构成,其中 '?' 可以替换成 '0''1'。假设 '?' 的个数为 p,则共有 2^p 种不同的字符串。需要计算所有 2^p 个字符串对应的 val(s) 之和,并对 \(10^9+7\) 取模。

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int n;
string s;
int ans,cnt;
vector<int> pos;
const int mod=1e9+7;
void dfs(string &s,vector<int> &pos,int num){
if (num==cnt){//如果?全部填充完毕
for (int i=0;i<n;i++){//枚举每个数的情况
if (s[i]=='0') s[i]='1';//替换
else s[i]='0';
int a=0,b=0;
for (int j=0;j<n-1;j++){
if (s[j]=='1'&&s[j+1]=='0') a++;
if (s[j]=='0'&&s[j+1]=='1') b++;
}
if (a==b){
ans++;
ans%=mod;
}
if (s[i]=='0') s[i]='1';
else s[i]='0';
}
return;
}
s[pos[num]]='0';//将该位置上的?替换为0
dfs(s,pos,num+1);
s[pos[num]]='1';//将该位置上的?替换为1
dfs(s,pos,num+1);
}
void solve(void){
pos.clear();
cnt=0;
ans=0;
cin>>n>>s;
for (int i=0;i<n;i++){
if (s[i]=='?'){
cnt++;
pos.push_back(i);
}
}
dfs(s,pos,0);
cout<<ans<<endl;
}
int main()
{
cintie;
int T;
cin >> T;
while (T--)
solve();
return 0;
}

简单版本的数据范围小,打DFS枚举每种情况就能过,时间复杂度最坏情况为 \(O(2^n\cdot n^2)\)

C:

相比于B题,扩大了数据范围,DFS过不了

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int mod=1e9+7;
void solve(void){
int n;
cin>>n;
string s;
cin>>s;
if (n==1){
if (s[0]=='?')
cout<<2<<endl;
else
cout<<1<<endl;
return;
}
int cnt=count(s.begin(),s.end(),'?');
//cout<<"cnt:"<<cnt<<endl;
long long ans=0;
long long npow=1;
for (int i=1;i<=cnt;i++){
npow*=2;
npow%=mod;
}
long long npow_1=1;
for (int i=1;i<=cnt-1;i++){
npow_1*=2;
npow_1%=mod;
}
if (s[0]!='?'&&s[n-1]!='?'){
if (s[0]==s[n-1]) ans=1ll*npow*(n-2)%mod;
else ans=1ll*npow*2%mod;
}
else ans=1ll*npow_1*n%mod;
cout<<ans<<endl;
}
int main()
{
cintie;
int T;
cin >> T;
while (T--)
solve();
return 0;
}

需要知道两个规律:但是我不知道

  • 首尾相同的字符串总是平衡的
  • 首尾不同的字符串不会是平衡的

分类讨论:

  • 只有一个字符时:如果不能为 ? 那么答案就是 \(2\) 否则为 \(1\)
  • 首尾都确定时:判断首尾是否相同,相同则为 \(2^n\cdot(n-2)\) 否则为 \(2^n\cdot(2)\)
  • 首尾不确定时:
    • 有一个确定:两种情况综合有,\(2^{n-1}\cdot (n-2+2)\)
    • 两个都不确定:四种情况综合有,\(2^{n-2}\cdot(n-2+2+n-2+2)=2^{n-1}\cdot (n-2+2)\)

D:

题目大意:给出两个字符串,对它们分别进行重新排列和替换字符的操作后,求使 s=a+b 为回文串的最少替换次数

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int mod=1e9+7;
void solve(void){
int n,m;
cin>>n>>m;
string a,b;
cin>>a>>b;
int sum=0;
if (n>m) swap(a,b),swap(n,m);//m>n
map<char,int> mp;
for (auto c:b) mp[c]++;
for (auto c:a) {
if (mp[c]) mp[c]--;
else sum++;
}
int ans=0;
for (auto [key,val]:mp)
if (val%2==1) ans++;
if (sum>=ans) ans=sum;
else{
ans-=sum;
ans/=2;
ans+=sum;
}
cout<<ans<<endl;
}
int main()
{
cintie;
int T;
cin >> T;
while (T--)
solve();
return 0;
}

a,b 原始串可能会存在能直接回文消除的字符,这里保证 b 串为短串,优先将短串的字符消去

for (auto c:b) mp[c]++;
for (auto c:a) {
if (mp[c]) mp[c]--;
else sum++;
}

如果短串存在的字符长串中没有,那么这个字符一定需要在长串中被替换到,记录这个数量

然后考虑长串中能相互消除的元素,即某种字符的数量为偶数,那么可以通过排序自己消去

for (auto [key,val]:mp)
if (val%2==1) ans++;

记录字符数量为奇数的种数,它们也需要通过替换才能消去

结果分为两种情况:

  • sum>=ans ,需要通过替换才能消去短串的字符数大于了长串中需要替换自我消去的种数,那么合并这两种操作,即最少都需要sum 次操作,才能使合并后的字符串回文
  • sum<ans ,长串中消去了短串的字符后,任然需要自我消除,那么计算答案为 \(sum+\frac{ans-sum}{2}\)
if (sum>=ans) ans=sum;//至少要修改短串中有但长串没有的元素个数那么多个
else{//ans消去了sum个,剩下的元素还需要两两相消
ans-=sum;
ans/=2;
ans+=sum;
}

A:

题目大意:存在正整数序列 a,其中每个元素都给出取值范围,求

\[\sum_{i=2}^n \lvert a_{i-1}-a_{i}\rvert \]

的期望值,最终结果为分数 \(\frac{p}{q}\) ,给出 \(p\cdot q^{-1} mod\ M\)\(q\) 满足同余式 \(q\times q^{-1}\equiv1(mod \ M)\)

挖坑后补

L:

题目大意:给定两个序列 a,b 每次询问区间 l,r 计算

\[\sum_{i=l}^r\sum_{j=i}^ra_i\oplus b_j \]

对答案取 1e9+7 的模后输出

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int mod=1e9+7;
void solve(void){
int n,q;
cin>>n>>q;
vector<int> a(n+5),b=a;
vector<vector<int>> fa(n+1,vector<int>(31)),fb=fa;
vector<int> f(n+5);
for (int i=1;i<=n;i++){
cin>>a[i];
fa[i]=fa[i-1];
for (int j=0;j<=30;j++)
if(a[i]&(1<<j))
fa[i][j]++;
}
for (int i=1;i<=n;i++){
cin>>b[i];
fb[i]=fb[i-1];
for (int j=0;j<=30;j++)
if(b[i]&(1<<j))
fb[i][j]++;
}
for (int i=n;i>=1;i--){
f[i]=f[i+1];
for (int j=0;j<=30;j++){
int b1=fb[n][j]-fb[i-1][j];
int b0=(n-i+1)-b1;
if (a[i]&(1<<j))
f[i]+=(1ll<<j)*b0%mod;
else
f[i]+=(1ll<<j)*b1%mod;
f[i]%=mod;
}
}
while (q--){
int l,r;
cin>>l>>r;
auto ans=(f[l]-f[r+1]+mod)%mod;
for (int i=0;i<=30;i++){
int a1=fa[r][i]-fa[l-1][i];
int b1=fb[n][i]-fb[r][i];
int a0=(r-l+1)-a1;
int b0=(n-r)-b1;
auto t=1ll*a1*b0+1ll*a0*b1;
ans-=(t%mod)*(1<<i)%mod;
ans=(ans+mod)%mod;
}
cout<<ans<<endl;
}
}
int main()
{
cintie;
int T;
cin>>T;
while (T--)
solve();
return 0;
}

需要知道关于位运算的方程都能够进行按位操作

首先分解需要求解的式子

\[\sum_{i=l}^r \sum_{j=i}^r a_i\oplus b_j \\ \implies\sum \left\{ \begin{array}{c} a_l\oplus (b_l+b_{l+1}+\cdots+b_r)\\ a_{l+1}\oplus(b_{l+1}+b_{l+2}+\cdots b_r)\\ \cdots\\ a_{r}\oplus b_r \end{array} \right. \]

观察展开后的方程,知道需要预处理 \(\sum_{j=1}^n b_j\) 的所有后缀和,那么 \(\sum_{j=i}^r b_j\) 都可以 \(O(1)\) 计算出来

然后考虑如何计算 \(xor\) 的操作,需要对数进行按二进制拆分,分别得到每位上的01值,举例说明:

for (int i=1;i<=n;i++){
cin>>b[i];
fb[i]=fb[i-1];
for (int j=0;j<=30;j++)
if(b[i]&(1<<j))
fb[i][j]++;
}

\(b_1=29,b_2=24\),二进制表示为 \(b_1=11101,b_2=11000\)\(fb_{i,j}\) 表示前 \(i\)\(b\) 在第 \(j\) 位上的前缀和(有多少个 \(1\))

\[\begin{array}{|c|c|c|} \hline fb&fb_{1,j}&fb_{2,j}&\cdots\\ \hline fb_{i,1}&1&1&\cdots\\ fb_{i,2}&0&0&\cdots\\ fb_{i,3}&1&1&\cdots\\ fb_{i,4}&1&2&\cdots\\ fb_{i,5}&1&2&\cdots\\ fb_{i,6}&0&0&\cdots\\ \cdots&\cdots&\cdots&\cdots \end{array} \]

\(\sum_{j=i}^r b_j\) 作为一个整体,考虑这个数与 \(a_l\)\(xor\) 计算,\(f_i\) 表示 \(a_i\oplus\sum_{j=i}^r b_j=a_i\oplus(fb_r-fb_{i-1})\)

\[\begin{array}{|c|c|c|c|c|c|c|c|} \hline \ &b_l&b_{l+1}&b_{l+2}&b_{l+3}&b_{l+4}&b_{l+5}&\cdots\\ \hline a_l&+&+&+&+&+&+&\cdots\\ \hline a_{l+1}&&+&+&+&+&+&\cdots\\ \hline a_{l+2}&&&+&+&+&+&\cdots\\ \hline a_{l+3}&&&&\cdots&\cdots&\cdots&\cdots\\ \hline \end{array} \]

我们最终需要预处理出来的是上面表格内为 \(+\) 的部分,横向相加的部分利用前缀和已经求得,那么问题就变为了如何计算 \(xor\) 了,仍然需要按位拆分计算

\(\sum_{j=i}^r b_j\) 这个整体是呈现后缀和的形式,我们计算后缀和后与 \(a\) 的每一位都分别做 \(xor\) 计算(相同为 \(0\),不同为 \(1\)

\[\begin{array}{|c|c|c|c|c|} \hline a_i&0&1&0&0&1&\cdots\\ \hline \sum_{j=i}^nb_j&2&4&0&3&0&\cdots\\ \hline a_i\oplus\sum_{j=i}^n b_j&(1\oplus0)\times(2)&(1\oplus0\times(r-i+1-4))&\cdots&\cdots&\cdots&\cdots\\ \hline \end{array} \]

\(\sum_{j=i}^nb_j\) 表明 \(b\) 的后缀和数中每个位上有多少个 \(1\)

for (int i=n;i>=1;i--){
f[i]=f[i+1];//计算a xor sum_b 的后缀和
for (int j=0;j<=30;j++){
int b1=fb[n][j]-fb[i-1][j];//计算b在i~n上第j位有多少个1
int b0=(n-i+1)-b1;//计算b在i~n上第j位有多少个0
if (a[i]&(1<<j))//计算异或
f[i]+=(1ll<<j)*b0%mod;//a在第j位为1,那么xor后的值为10的对数
else
f[i]+=(1ll<<j)*b1%mod;//a在第j位为0,那么xor后的值为01的对数
f[i]%=mod;
}
}

综上,我们已经将所有的预处理工作做完了,从下往上地计算出了横向绿色块的后缀和(以 \(n=9\) 为例)

那我们需要求解的值在图中一定能被表示为一个三角形(黄色表示)

\[\sum_{i=l}^r\sum_{j=i}^ra_i\oplus b_j \]

通过我们已知的 \(f\) 后缀和可以 \(O(1)\) 计算出图中梯形的数值(红框包围)

那么再通过已知的 \(fb,fa\) 前缀和又可以得到右侧矩形的数值,两者相减就能得到所需三角形的数值

auto ans=(f[l]-f[r+1]+mod)%mod;//计算梯形的总值
for (int i=0;i<=30;i++){
int a1=fa[r][i]-fa[l-1][i];//右侧矩形范围内a在l~r中的第j位上1的个数
int b1=fb[n][i]-fb[r][i];//右侧矩形范围内b在r~n中的第j位上1的个数
int a0=(r-l+1)-a1;//右侧矩形范围内a在l~r中的第j位上0的个数
int b0=(n-r)-b1;//右侧矩形范围内a在r~n中的第j位上0的个数
auto t=1ll*a1*b0+1ll*a0*b1;//按位计算右侧矩形的总值
ans-=(t%mod)*(1<<i)%mod;//减去矩形分量
ans=(ans+mod)%mod;//取正数
}
posted @   才瓯  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示