牛客周赛78文字版题解

A.时间表查询

寒假营比完了两场,所以当 输出 YES,否则输出 NO。

时间复杂度:

void solve(){
    int x;
    cin>>x;
    if(x==1||x==2){
        cout<<"YES";
    }else{
        cout<<"NO";
    }
}

B.一起做很甜的梦!

ps:本来是给寒假营2的,有点原了,所以给周赛签到了。

考虑区间长度为 的都不能构成排列,那么这些排列所组成的数都包含 两个数字,所以只需要令 ,其他的由 任意填入即可。

时间复杂度:

void solve(){
    int n;
    cin>>n;
    cout<<1<<" ";
    for(int i=n;i>=2;i--){
        cout<<i<<" ";
    }
}

C.翻之

我们考虑每列组成的字符串为 ,一行的翻转必然会影响其他列,所以只有当两列的字符串完全相同时,才会同步变,所以就考虑列组成的字符串中出现次数最多的。

时间复杂度:

void solve(){
    int n,m;
    cin>>n>>m;
    vector<string>s(n+1);
    for(int i=1;i<=n;i++) cin>>s[i],assert(s[i].size()==m),s[i]=" "+s[i];
    map<string,int>cnt;
    for(int j=1;j<=m;j++){
        string t="";
        for(int i=1;i<=n;i++){
            t+=s[i][j];
        }
        cnt[t]++;
    }
    int res=0;
    for(auto [s,g]:cnt){
        res=max(res,g);
    }
    cout<<res;
}

D.乘之

update:2025/1/25 22:10 ps:写得过于抽象了,所以换个讲题人的证明吧。

先确定操作一个区间是 ,先证明两个人操作的区间一定是包含关系或者是连续的关系,因为如果不连续那么两个区间中间的区间和要么>=0让第一个人可以选,要么是<0让第二个人选,所以一定是连续的更优,所以相当于两个人操作一个区间,接下来证明这个区间是整个区间,考虑选了个区间 ,对于左侧如果区间和>=0那么第一个人会选,如果<0那么第二个人会选,右侧情况相同,所以一定是操作整个区间

时间复杂度:

void solve(){
    int n,k;
    cin>>n>>k;
    i64 ans=0;
    for(int i=1;i<=n;i++){
        i64 x;
        cin>>x;
        ans+=(k*x);
    }
    cout<<ans<<"\n";
}

E.在树上游玩

本质上就是一些关键点需要引出一条边到非关键点,由于 ,就只需要对每个关键点联通块引出一点红色边即可,所以最小染色代价就是连通块的个数。

而染色方案就是每个关键点连通块中所接的白边条数之积。

时间复杂度:

const int mod=1e9+7;
void solve(){
    int n,k;
    cin>>n>>k;
    vector<int>vis(n+1,0);
    vector<int>p(n+1);
    for(int i=1;i<=k;i++){
        int x;
        cin>>x;
        vis[x]=1;
    }
    iota(p.begin(),p.end(),0);
    auto find=[&](auto &&self,int x)->int {
        if(p[x]==x) return x;
        p[x]=self(self,p[x]);
        return p[x];
    };
    vector<int>cnt(n+1);
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        if(vis[u]==1&&vis[v]==0){
            cnt[find(find,u)]++;
        }else if(vis[u]==0&&vis[v]==1){
            cnt[find(find,v)]++;
        }else if(vis[u]==1&&vis[v]==1){
            int fu=find(find,u);
            int fv=find(find,v);
            cnt[fv]+=cnt[fu];
            p[fu]=fv;
        }
    }
    i64 res1=0,res2=1;
    for(int i=1;i<=n;i++){
        if(find(find,i)==i&&vis[i]){
            res1++;
            res2=res2*cnt[i]%mod;
        }
    }
    cout<<res1<<" "<<res2; 
}

F.小红的01子序列计数(hard)

我们考虑以每个位置 开始的子串包含01子序列的个数。

首先答案由以下部分组成: 1. 这部分只需要加上自身区间的答案,即该区间中的每个 的区间长度乘上 到该位置 的个数 。 然后这段还可以贡献的长度还有 ,此部分的答案就是该区间的 子序列个数乘以

2. 的部分就是用每个 所在位置到 的长度乘上字符串头部到该位置 的个数,然后再减去 子序列乘以 多贡献的这部分。

3. 中的 中的 相互搭配的贡献: 即 的个数乘以 ( 中每个 '1' 的位置到 的距离和)。 然后需要减掉这段搭配区间的 子序列个数乘以 的贡献。

时间复杂度:

这样就大功告成! 之所以要每个位置扣掉,是因为预处理方便。 如果代码看不懂,可以先把 mod 全部删掉后,会清晰一点。

const int mod=1e9+7;
void solve(){
    int n;
    string s;
    cin>>n>>s;
    s=" "+s;
    vector<i64>num0(n+2),num1(n+2),num01(n+2);
    vector<i64>pre_gx1(n+2);
    for(int i=1;i<=n;i++){
        num0[i]=num0[i-1]+(s[i]=='0');
        num1[i]=num1[i-1]+(s[i]=='1');
        num01[i]=num01[i-1]+(s[i]=='1')*num0[i]%mod;
        num01[i]%=mod;
        pre_gx1[i]=pre_gx1[i-1]+(s[i]=='1')*(n-i+1)%mod;
        pre_gx1[i]%=mod;
    }
    vector<i64>gx(n+2);
    for(int i=1;i<=n;i++){
        gx[i]=gx[i-1]+(s[i]=='1')*num0[i]*(n-i+1)%mod;
        gx[i]%=mod;
    }
    // 以 i 为起点
    i64 ans=0;
    for(int i=1;i<=n;i++){
        // 先选 [i,n] 这部分自己的答案
        i64 part1=(gx[n]-gx[i-1])-(pre_gx1[n]-pre_gx1[i-1]+mod)*(num0[i-1])%mod;
        part1%=mod;
        
        //算 [i,n] 这部分 01 还可以贡献的区间长度
        i64 part2= part1+(num01[n]-num01[i-1]+mod-num0[i-1]*(num1[n]-num1[i-1]+mod)%mod)*(i-1)%mod;   //是最后的答案的部分
        part2%=mod;
        // 算 [1,i-1] 这部分自己的答案
        i64 part3=gx[i-1];
        //算 [1,i-1] 这部分需要扣掉的贡献长度
        i64 part4=part3-num01[i-1]*(n-i+1)%mod;
        part4=(part4+mod)%mod;
        //算 [i,n] 这部分的 0 和 [1,i-1] 这部分的 1 的贡献
        i64 part5= (num0[n]-num0[i-1]+mod)%mod*(pre_gx1[i-1])%mod;
        // 扣掉多贡献的部分
        part5=part5-(num0[n]-num0[i-1]+mod)%mod*(n-i+1)%mod*(num1[i-1])%mod;
        ans+=part2+part4+part5;
        ans%=mod;
    }
    cout<<ans;
}
全部评论
不是??d题为啥每个数都会被选啊?? 如果我输入的是-5 10 -10 -10 然后k取5 很显然 最大区间为10 最小的区间为-10,-10 那总和不就是-50吗?? 按照这里的题解,那答案不就变成-75了吗??
2 回复 分享
发布于 01-25 21:32 安徽
D题的测试数据 5 2 1 -10 3 -10 1 怎么解释
2 回复 分享
发布于 01-25 21:34 浙江
眼睛尿尿了,全是这种码量极少的思维题,但就是不会
1 回复 分享
发布于 01-25 21:47 广东
感谢出题人,终于不是ABC+0.5D选手了
1 回复 分享
发布于 01-26 00:52 江西
D为啥全选??没懂
点赞 回复 分享
发布于 01-25 21:12 河北
哭哭了
点赞 回复 分享
发布于 01-25 21:14 广西
D啥意思啊
点赞 回复 分享
发布于 01-25 21:15 河北
这个d是说,如果a选一段贡献是正的的区间,那两侧贡献是负的区间b会选,然后再外侧贡献是正的a又能选,然后依次类推,所有区间都应该选
点赞 回复 分享
发布于 01-25 21:58 北京
好似没人理解初始题解,更新了D题的证明。
点赞 回复 分享
发布于 01-25 22:15 福建
我想问一下 C题的关键是不是看每列相同的最多有几个?
点赞 回复 分享
发布于 01-25 22:53 河南
桥杯思维题也这么多吗,www真想不到
点赞 回复 分享
发布于 01-26 09:38 山东
接好运
点赞 回复 分享
发布于 02-13 11:32 安徽

相关推荐

评论
7
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务