随笔 - 13  文章 - 0  评论 - 7  阅读 - 487

Codeforces Round #833 (Div. 2) A-F

Codeforces Round #833 (Div. 2) 做题记录

A.The Ultimate Square

略过

B.Diverse Substrings

思路:

我们发现字符数只有0~9十种字符,也就是说,如果我们固定一个左端点l,那么最多向右扩展1010次后就会不满足条件,直接暴力枚举即可

#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

题意:

有一个数组ai,长度为n

你可以将其中ai为0的值改为任意数。求最后满足i=1kai=0k的最多个数

(1n2105,109ai109),均为整数

思路:

易知,每两个零都是独立的。上一个0的取值并不会影响到下一个0的取值。

我们设p1,p2ai为0的位置,且p1<p2p1,p2在0的位置上相邻

那么,在p1时,我们需要选择一个数字,使得p1p21中满足条件的k最多,实际上就是找一个前缀和众数,找到sum[p1]sum[p21]中出现最多的一个数字,我们就可以通过调整0达到目的。sum[i]=k=p1iak

#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

题意:

有三个整数a,b,d,求一个整数x,使得其满足以下三个条件:
0x<260

a|x能被d整除

b|x能被d整除

1a,b,d<230

其中的|符号代表或运算

思路:

我们先考虑无解的情况,先考虑特殊情况。

易知,当ab为奇数,而d为偶数的时候一定无解

我们将这个式子推广一下,若a,b,d不成立,那么2a,2b,2d也定然不成立,相当于你把这三个数左移一位,不会有影响。

那么,我们初步无解的条件就判断出来了:当min(low(a),low(b))<low(d)时一定无解,其中low(x)代表的是x最右边一位为1的位置

我们设a,b,d最右边k个数均为0,那么a,b,d有效位置便为(30k)位 不妨将这些数先全部右移k位,得到a,b,d

我们考虑一种构造方法:使得a|x=x,b|x=x

那么在右移的数上便是使得a|x=x,b|x=x

我们可以先设置x=230k1,这样就保证了上述两个条件

那么我们还要满足p230k+230k1d的倍数,便是求一个p出来

将式子化简:(p+1)230k1d的倍数,等价于(p+1)230k1(modd)

有点不直观?我们再化简一下,设a=p+1,x=230k,则原式变成ax1(modd)

直接扩展欧几里得求解即可。记得a要大于-1

#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

题意:

数组x=[x1,x2,...,xn][l;r]段上最左边的最大值的位置是最小的整数i,使得lirxi=max(xl,xl+1,...,xr)

给你一个长度为n的数组a=[a1,a2,...,an],求满足下列条件的长度为n的整数组b=[b1,b2,...,bn]的数量。

1bim,所有1in
对于所有一对整数1lrn,数组b[l;r]段上最左边的最大值的位置等于数组a[l;r]段上最左边的最大值的位置。
由于答案可能非常大,请打印其余数模109+7

2n,m2105,nm106,aim,均为整数

思路:

我们考虑到底什么样的b序列才能合法

不妨先设mai的最大值且最靠前的位置,i[1,n]

f(l,m1)=p1=max(ai),i[1,m1] f(m+1,n)=p2=max(ai),i[m+1,n]

那么先决条件便是b[p1]<b[m],b[p2]b[m]

在满足的情况下,这个序列便一分为二,可以继续递归求解

我们不妨构造一棵二叉树,对于节点x=f(l,r),他的两个儿子分别是f(l,x1)f(x+1,r),对于每一个x[1,n]

当然,不是所有的节点都有两个儿子。对于x=l的点,没有左儿子;对于x=r的点,没有右儿子。

我们设一个dp[i][j]表示为目前是第几个数 这个数的大小是多少。自然,我们的转移是根据我们这棵树的结构来确定的,这是一个树形DP

具体而言:

如果x有一个左边的孩子且x=1,那么dp[x][val]=0
否则,如果x有两个孩子,那么dp[x][val]=i=1x1dp[p1][i]i=1xdp[p2][i]
如果x只有一个左边的孩子,那么dp[x][val]=i=1x1dp[p1][i]
如果x只有一个右边的孩子,那么dp[x][val]=i=1xdp[p2][i]
如果x没有孩子,那么dp[x][val]=1

当然,我们发现这样时间是过不了的。但是对于求和操作我们可以使用前缀和数组sum来优化即可通过此题

#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

题意:

你有一个长度为n的数组a0,a1,...,an1,初始时,ai=2i,所有0i<n。注意,数组a是零指数的。

你想反转这个数组(也就是说,让ai等于2n1i,所有0i<n)。要做到这一点,你可以执行以下操作,不超过250000次。

选择一个整数i0i<n,用aia(i+1)modn替换ai
这里,表示位XOR操作。

你的任务是找到任何能使数组a被反转的操作序列。可以证明,在给定的约束条件下,一个解决方案总是存在的。

思路:

我们不妨试着用以下方法构造:

先定义一个函数f(i,j)表示为从i开始到j结束把这个区间里的数拿出来进行计算。

注意,我们使得an1a0相邻

举个例子,假设现在我们的n是4,那么其对应的a数组应该是1,2,4,8

那么函数f(0,3)就是拿出1,2,4,8这四个数来,f(3,1)就是拿出8,1,2三个数(有序

为了方便起见,我们设立一个数组b用来存储取出来的a,更形式化一点:

m=(ij+n)modn

bk=a(i+k)modn,0k<m

考虑如何计算,我们假设现在我们可以使得bi=bixorbmi1,0i<m+12

对于n为偶数的情况,我们可以依次执行f(0,n1),f(n2,n21),f(0,n1)使得其倒序。

为什么?我们可以模拟一下,假设n等于4:
第一次:a1,a2,a3,a4 变成 a1xora4,a2xora3,a3,a4

第二次:b序列:a3,a4,a1xora4,a2xora3 变成 a1,a2,a1xora4,a2xora3

然后映射到a上就是a1xora4,a2xora3,a2,a1

第三次就可以变成a4,a3,a2,a1

显然,这种方法对任意一种偶数都成立

我们考虑奇数的情况:可以依次执行f(0,n1),f(n+12,n32),f(0,n1)

模拟方法如上,这种方法仍然对任意一种奇数都成立

bi=bixorbmi1成立也很简单,我们先让biimi1的前缀异或值。自然,0i<m+12

在全部计算完之后执行bi=bixorbi+1即可做到。(关于次数的证明放在代码里面了)

#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;
}
posted on   C2023CYJ  阅读(59)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示