牛客周赛68文字版题解

A.三途川的摆渡人(二)

求给定的字符串有多少个 0,直接统计即可。

void solve(){
    int n;
    cin>>n;
    string s;
    cin>>s;
    cout<<count(s.begin(),s.end(),'0');
}

B 魔法之森的蘑菇(二)

只需要枚举左上角和右下角,然后遍历查询该矩形内有没有 * 即可,然后当查询到更大的矩阵时依次更新下标。

由于 ,所以 可以通过,也可以用二维前缀和优化,时间复杂度

void solve(){
    int n,m;
    cin>>n>>m;
    vector<string>a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        a[i]='%'+a[i];
    }
    vector<vector<int>>s(n+1,vector<int>(m+1));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(a[i][j]=='.');
        }
    }
    auto get=[&](int a,int b,int c,int d){
        return s[c][d]-s[c][b-1]-s[a-1][d]+s[a-1][b-1];
    };
    int ansx1,ansy1,ansx2,ansy2,smax=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int x=i;x<=n;x++){
                for(int y=j;y<=m;y++){
                    if(get(i,j,x,y)==(x-i+1)*(y-j+1)){
                        if((x-i+1)*(y-j+1)>smax){
                            smax=(x-i+1)*(y-j+1);
                            ansx1=i;
                            ansy1=j;
                            ansx2=x;
                            ansy2=y;
                        }
                    }
                }
            }
        }
    }
    cout<<ansx1<<" "<<ansy1<<" "<<ansx2<<" "<<ansy2;
}

C 迷途之家的大贤者(二)

首先进行操作一,两个容器要先计算自己消除的次数,分别为

此时得到两个无重集 ,两个容器可能不等长,存在相等的元素。

我们现在要消除相同元素,先统计相同元素,当存在相同元素时,我们可以在操作一中次数较少的时候同时消除此元素。

,则说明需要另外的次数去操作,由于两个人是同时操作,所以相同的元素可以同时消除 个,例如: ,那第一个人可以消除 ,第二个人可以消除 ,这样 ,当然有可能只存在奇数个相等元素,所以额外的次数除以 时要向上取整。

void solve(){
    int n;
    cin>>n;
    vector<int>a(n),b(n);
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++) cin>>b[i];
    vector<int>p,q;
    map<int,int>cnt;
    int ans1=0,ans2=0;
    for(int i=0;i<n;i++){
        if(cnt[a[i]]==0){
            p.push_back(a[i]);
        }else{
            ans1++;
        }
        cnt[a[i]]++;
    }
    cnt.clear();
    for(int i=0;i<n;i++){
        if(cnt[b[i]]==0){
            q.push_back(b[i]);
        }else{
            ans2++;
        }
        cnt[b[i]]++;
    }
    cnt.clear();
    for(int i=0;i<p.size();i++) cnt[p[i]]++;
    for(int i=0;i<q.size();i++) cnt[q[i]]++;
    int ans=0;
    for(auto t:cnt){
        if(t.second==2){
            if(ans1>ans2) ans2++;
            else if(ans1<ans2) ans1++;
            else ans++;
        }

    }
    ans=(ans+1)/2;
    if(ans==0){
        cout<<max(ans1,ans2);
    }else{
        cout<<ans1+ans;
    }
}

D.红魔馆的馆主(二)

:代码量的题,复制粘贴效率最快

分解后 ,先考虑不 的情况,直接算出对数,不能 枚举,对于一个数我们只需要算出这个数缺多少个 ,然后找到前缀中有多少个满足乘上后可以是 的倍数。

的情况同上算一下,然后记得先减去之前的贡献,取较大值的答案即可,算完之后再恢复不 的贡献。

int cnt[2][2][3];
void solve(){
    int n;
    cin>>n;
    vector<i64>a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    i64 ans=0;
    for(int i=1;i<=n;i++){
        int cnt5=0,cnt11=0,cnt3=0;
        int temp=a[i];
        while(temp%5==0) temp/=5,cnt5++;
        while(temp%11==0) temp/=11,cnt11++;
        while(temp%3==0) temp/=3,cnt3++;
        ans+=cnt[max(1-cnt5,0)][max(1-cnt11,0)][max(2-cnt3,0)];
        for(int x=0;x<=min(1,cnt5);x++){
            for(int y=0;y<=min(1,cnt11);y++){
                for(int z=0;z<=min(2,cnt3);z++){
                    cnt[x][y][z]++;
                }
            }
        }
       
    }
    //考虑当前元素加 1
    i64 fans=ans;
    for(int i=1;i<=n;i++){
        int cnt5=0,cnt11=0,cnt3=0;
        int temp=a[i];
        while(temp%5==0) temp/=5,cnt5++;
        while(temp%11==0) temp/=11,cnt11++;
        while(temp%3==0) temp/=3,cnt3++;
        for(int x=0;x<=min(1,cnt5);x++){
            for(int y=0;y<=min(1,cnt11);y++){
                for(int z=0;z<=min(2,cnt3);z++){
                    cnt[x][y][z]--;
                }
            }
        }   
        i64 res=ans-cnt[max(1-cnt5,0)][max(0,1-cnt11)][max(2-cnt3,0)];
        temp=a[i]+1;
        cnt3=cnt5=cnt11=0;
        while(temp%5==0) temp/=5,cnt5++;
        while(temp%11==0) temp/=11,cnt11++;
        while(temp%3==0) temp/=3,cnt3++;
        res+=cnt[max(1-cnt5,0)][max(1-cnt11,0)][max(2-cnt3,0)];  
        fans=max(fans,res);
        temp=a[i];
        cnt3=cnt5=cnt11=0;
        while(temp%5==0) temp/=5,cnt5++;
        while(temp%11==0) temp/=11,cnt11++;
        while(temp%3==0) temp/=3,cnt3++;
        for(int x=0;x<=min(1,cnt5);x++){
            for(int y=0;y<=min(1,cnt11);y++){
                for(int z=0;z<=min(2,cnt3);z++){
                    cnt[x][y][z]++;
                }
            }
        }
    }
    cout<<fans;
}

E.博丽神社的巫女(二)

我们用数组 考虑枚举到第 个数且当前 通过操作可以凑出 的可行性。

那么初始状态就是

通过枚举每个位置操作多少次,不然发现最多 次,所以直接枚举操作次数以及凑出的数字,假设当前除完的数字是 ,那么

最后即可判断是否能出

由于要输出方案,所以我们记录 的转移路径,考虑当前第 个位置凑出的数字是由 总和为多少转移过来的,即

最后从最后一个位置还原回去即可,看当前这个数是需要多少,即代码中的 然后把 一直除到 ,同时维护操作次数和当前前缀总和即可。

i64 dp[101][100001];
int pos[101][100001];
void solve(){
    int n;
    cin>>n;
    vector<i64>a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        int temp=a[i];
        int cnt=0;
        for(int j=0;cnt<2;j++){
            for(int k=100000;k>=temp;k--){
                if(dp[i][k]==0&&dp[i-1][k-temp]!=0){
                    pos[i][k]=k-temp;
                }
                dp[i][k]|=dp[i-1][k-temp];

            }
            temp/=2;
            if(temp==0) cnt++;
        }
    }
    if(dp[n][100000]==0){
        cout<<"-1";
        return ;
    }

    int s=100000;
    vector<int>ans(n+1);
    int idx=n;
    while(1){
        int temp=pos[idx][s];
        int c=s-temp;
        while(a[idx]!=c) a[idx]/=2,ans[idx]++;
        idx--;
        s=s-c;
        if(idx==0) break;

    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}

F.雾之湖的冰精(三)

对于一条路径仅可能存在两种情况,即 alt

先考虑第一种,即 可以直接从 的路径总数转移过来。

定义 为结点 为路径的一个端点,且路径总和不超过 的方案总数,那么状态便可以从 转移过来。然后每到一个结点把答案加上。

对于第二种情况,就是第一种中的乘法原理,从中任选一条路径给 ,然后转移就可以。

每到一个结点,统计以 为端点且总和不超过 的路径总数,记 的同父结点下,路径总和不超过 的路径总数。 那么对于 而言就是加上 ,其中 ,这里记得把 算进去,因为它也在路径里。

void solve(){
    int n;
    cin>>n;
    vector<int>a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    vector<vector<int>>e(n+1);
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    vector<vector<i64>>dp(n+1,vector<i64>(10));
    i64 ans=0;
    auto dfs=[&](auto &&self,int u,int fa)->void {
        vector<i64>s(10);
        for(auto v:e[u]){
            if(v==fa) continue;
            self(self,v,u);
            for(int i=a[u];i<=9;i++){
                dp[u][i]=dp[u][i]+dp[v][i-a[u]];  
            }
            for(int i=0;i<=9;i++){
                for(int j=0;j<=9-i-a[u];j++)
                ans+=dp[v][i]*s[j];
            }
            for(int i=0;i<=9;i++){
                s[i]+=dp[v][i];
            }
        }
        for(int i=0;i<=9;i++) ans+=dp[u][i];
        dp[u][a[u]]++;
    };
    dfs(dfs,1,0);
    cout<<ans;
}
全部评论
%%%%%%%
点赞 回复 分享
发布于 11-18 09:27 河南
b题j应该小于等于m吧?
点赞 回复 分享
发布于 11-20 14:29 辽宁
对于E题中  for(int k=100000;k>=temp;k--){} 这样为什么不会因为操作次数太多而T了呢,毕竟tem会/2,最坏情况可能会有几千万甚至上亿吧? 后面我想了一下应该可以通过这样减少很多操作吧 sum += a[i];         for (int j = 0; cnt < 2; j++)         {             for (int k = sum; k >= temp; k--){} 修改后也可以过。 本人菜狗,请求指教
点赞 回复 分享
发布于 11-20 22:02 湖南

相关推荐

不愿透露姓名的神秘牛友
11-04 13:11
点赞 评论 收藏
分享
评论
14
3
分享
牛客网
牛客企业服务