Codeforces Round #833 (Div. 2) A-F
Codeforces Round #833 (Div. 2) 做题记录
A.The Ultimate Square
略过
B.Diverse Substrings
思路:
我们发现字符数只有0~9十种字符,也就是说,如果我们固定一个左端点
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+10;
int T,n;
int a[maxn],t[maxn];
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);getchar();
for(int i=1;i<=n;i++){
char x;
scanf("%c",&x);
a[i]=x-'0';
}
int ans=0;
for(int i=1;i<=n;i++){
int l=i,sum=0,mx=0;
for(int j=0;j<10;j++)t[j]=0;
while(l<=n){
t[a[l]]++;
if(t[a[l]]==1)sum++;
mx=max(mx,t[a[l]]);
if(mx<=sum)ans++;
if(mx>10)break;
l++;
}
}
printf("%lld\n",ans);
}
return 0;
}
C.Zero-Sum Prefixes
题意:
有一个数组
,长度为 你可以将其中
为0的值改为任意数。求最后满足 的 的最多个数
,均为整数
思路:
易知,每两个零都是独立的。上一个0的取值并不会影响到下一个0的取值。
我们设
那么,在
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
#define int long long
int t,n;
int a[maxn],sum[maxn];
map<int,int>q;
signed main(){
cin>>t;
while(t--){
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
int ans=0,s=0;
for(int i=1;i<=n;i++){
if(a[i]!=0)s+=a[i];
else break;
if(s==0)ans++;
}
for(int i=1;i<=n;i++){
if(a[i]==0){
int l,r=-1;
l=i;
for(int j=i+1;j<=n;j++){
if(a[j]==0){r=j;break;}
}
if(r==-1)r=n+1;
sum[l-1]=0;
int flag=1;
for(int j=l;j<r;j++)sum[j]=sum[j-1]+a[j];
q.clear();int mx=0;
for(int j=l;j<r;j++)q[sum[j]]++,mx=max(mx,q[sum[j]]);
flag=flag+mx-1;
ans=ans+flag;
}
}
printf("%lld\n",ans);
}
return 0;
}
D.ConstructOR
题意:
有三个整数
,求一个整数 ,使得其满足以下三个条件:
能被 整除
能被 整除
其中的
符号代表或运算
思路:
我们先考虑无解的情况,先考虑特殊情况。
易知,当
我们将这个式子推广一下,若
那么,我们初步无解的条件就判断出来了:当
我们设
我们考虑一种构造方法:使得
那么在右移的数上便是使得
我们可以先设置
那么我们还要满足
将式子化简:
有点不直观?我们再化简一下,设
直接扩展欧几里得求解即可。记得
#include<bits/stdc++.h>
using namespace std;
#define int long long
int T,A,B,d;
int a,b,s1,s2;
int qpow(int x,int y){
if(y==0)return 1;
if(y&1)return x*qpow(x,y-1);
else return qpow(x*x,y/2);
}
void exgcd(int x,int y){
if(y==0){
s1=1,s2=0;
return ;
}
exgcd(y,x%y);
int temp=s1;
s1=s2;
s2=temp-s2*(x/y);
}
int work(int x){
for(int i=0;i<31;i++){
if((x>>i)&1){
return i;
}
}
}
void check(){
int x=min(work(A),work(B));
int y=work(d);
if(x<y){
printf("-1\n");
return ;
}
int k=min(x,y);
a=qpow(2,30-k);b=d/qpow(2,k);
exgcd(a,b);
int p=((s1-1)%b+b)%b;
int res=p*qpow(2,30-k)+qpow(2,30-k)-1;
printf("%lld\n",res*qpow(2,k));
}
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&A,&B,&d);
check();
}
return 0;
}
E.Yet Another Array Counting Problem
题意:
数组
的 段上最左边的最大值的位置是最小的整数 ,使得 且 。 给你一个长度为
的数组 ,求满足下列条件的长度为n的整数组 的数量。
,所有 。
对于所有一对整数,数组 的 段上最左边的最大值的位置等于数组 的 段上最左边的最大值的位置。
由于答案可能非常大,请打印其余数模。
,均为整数
思路:
我们考虑到底什么样的
不妨先设
设
那么先决条件便是
在满足的情况下,这个序列便一分为二,可以继续递归求解
我们不妨构造一棵二叉树,对于节点
当然,不是所有的节点都有两个儿子。对于
我们设一个
具体而言:
如果
否则,如果
如果
如果
如果
当然,我们发现这样时间是过不了的。但是对于求和操作我们可以使用前缀和数组
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
int t,n,m;
int a[maxn],tr[maxn<<2];
vector<int>dp[maxn];//位置 取值
void add(int l,int r,int rt,int x,int val){
if(l==r){
tr[rt]=val;
return ;
}
int mid=(l+r)/2;
if(x<=mid)add(l,mid,rt*2,x,val);
else add(mid+1,r,rt*2+1,x,val);
tr[rt]=max(tr[rt*2],tr[rt*2+1]);
}
int query(int l,int r,int rt,int L,int R){
if(L<=l&&r<=R){
return tr[rt];
}
int mid=(l+r)/2;
int mx=-1;
if(L<=mid)mx=max(mx,query(l,mid,rt*2,L,R));
if(R>mid)mx=max(mx,query(mid+1,r,rt*2+1,L,R));
return mx;
}
int cxk(int l,int r,int rt,int L,int R,int val){
if(l==r)return l;
int mid=(l+r)/2;
int id=-1;
if(L<=mid&&tr[rt*2]>=val)id=cxk(l,mid,rt*2,L,R,val);
if(R>mid&&tr[rt*2+1]>=val){
if(id==-1)id=cxk(mid+1,r,rt*2+1,L,R,val);//先左后右
}
return id;
}
int solve(int l,int r){
if(l>r)return -1;
int mid=cxk(1,n,1,l,r,(query(1,n,1,l,r)));//位置
int p1=solve(l,mid-1);
int p2=solve(mid+1,r);
for(int i=1;i<=m;i++){
int ans=0;
if(p1>=0&&i==1)ans=0;
else ans=((p1>=0)?dp[p1][i-1]:1ll)*((p2>=0)?dp[p2][i]:1ll)%mod;ans%=mod;
dp[mid][i]+=(dp[mid][i-1]+ans)%mod;
}
return mid;
}
void work(){
for(int i=1;i<=n;i++)add(1,n,1,i,a[i]);
for(int i=1;i<=n;i++)dp[i].resize(m+2);
for(int i=1;i<=n;i++)dp[i].clear();
solve(1,n);
int mid=cxk(1,n,1,1,n,(query(1,n,1,1,n)));
printf("%lld\n",dp[mid][m]);
}
signed main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
work();
}
return 0;
}
F.Circular Xor Reversal
题意:
你有一个长度为
的数组 ,初始时, ,所有 。注意,数组a是零指数的。 你想反转这个数组(也就是说,让
等于 ,所有 )。要做到这一点,你可以执行以下操作,不超过250000次。 选择一个整数
,用 替换 。
这里,表示位XOR操作。 你的任务是找到任何能使数组a被反转的操作序列。可以证明,在给定的约束条件下,一个解决方案总是存在的。
思路:
我们不妨试着用以下方法构造:
先定义一个函数
注意,我们使得
举个例子,假设现在我们的
那么函数
为了方便起见,我们设立一个数组
设
考虑如何计算,我们假设现在我们可以使得
对于
为什么?我们可以模拟一下,假设
第一次:
第二次:
然后映射到
第三次就可以变成
显然,这种方法对任意一种偶数都成立
我们考虑奇数的情况:可以依次执行
模拟方法如上,这种方法仍然对任意一种奇数都成立
让
在全部计算完之后执行
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>ans;
void add(int x){
ans.push_back(x%n);
}
void f(int l,int r){
if(r<l)r+=n;//模拟mod过程
int m=r-l,id=l,flag=0;
r--;//因为b序列的最后一个肯定是不用执行操作的
while(l<=r){
if(flag==0){//赋值 即前缀异或值
for(int i=r;i>=l;i--)add(i);
l++;
}
else{//复原 然后bi和bj就不用计算了,左右区间均缩小1
for(int i=l;i<=r;i++)add(i);
r--;
}
flag^=1;
}
for(int i=id;i<id+m/2;i++)add(i);
}
int main(){
scanf("%d",&n);
f(0,n-1);//n*(n-1)/2+n/2-1
f((n+1)/2,(n-2)/2);//<=n*(n-1)/2+n/2-1
f(0,n-1);//n*(n-1)/2+n/2-1
//总次数<=1.5n*(n-1)+1.5n-3<=240000次
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++)printf("%d ",ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)