Educational Codeforces Round 155 (Rated for Div. 2)

Problem - A - Codeforces

取x为s[1],保证第一个能举得动,然后x只能小于等于s[1],我们肯定越大越好,这样让别人举不动,所以当x取s[1]时都不能满足,那么就不可能满足了,如果能满足,那么s[1]就是其中的一个解

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
//#define int long long
using namespace std;
const int N=110;
int s[N],e[N];
int n;
bool check(int x){
    if(s[1]<x) return false;
    for(int i=2;i<=n;i++){
        if(s[i]>=x&&e[i]>=e[1]) return false;
    }
    return true;
}
void solve() {
    cin>>n;
    for(int i=1;i<=n;i++) cin>>s[i]>>e[i];
    if(check(s[1])) cout<<s[1]<<endl;
    else cout<<-1<<endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

Problem - B - Codeforces

贪心,题目要求放置的最低条件是要么每行都有一个,要么每列都有一个,由于要求最小的,所以分别考虑两种情况

a数组作为行,b数组作为列

每列都有一个,那么就是所有的b都取到,然后取最小的a和它们分别相加即可

每行都有一个,那么就是所有的a都取到,然后取最小的b和它们分别相加即可

然后取min

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {
    cin>>n;
    int suma=0,sumb=0;
    int mina=2e9,minb=2e9;
    for(int i=1;i<=n;i++) {
        int x;
        cin>>x;
        suma+=x;
        mina=min(mina,x);
    }
    for(int i=1;i<=n;i++) {
        int x;
        cin>>x;
        sumb+=x;
        minb=min(minb,x);
    }
    int ans;
    ans=min(mina*n+sumb,minb*n+suma);
    cout<<ans<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

Problem - C - Codeforces

一开始是这样想的,对于连续x个一样的数,需要删除x-1个数,种数为x的阶乘,可以通过dfs+记忆化搜索来快速得到阶乘

将一段段连续一样的数分别存储

比如11100

第一段为111,第二段为00,我们在111中选2个,在00中选一个组成110,然后删除这3个数的种数乘以C(3,2)乘以C(2,1)即可

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int mod=998244353;
unordered_map<int,int>mp;
int dfs(int n){
    if(n==0) return 1;
    if(n==1) return 1;
    if(mp.count(n)) return mp[n]%mod;
    return mp[n]=n*dfs(n-1)%mod;
}
string s;
void solve() {
    cin>>s;
    int n=s.size();
    vector<int>e(n+1);
    int cnt=-1;
    int sum=1;
    for(int i=1;i<s.size();i++){
        if(s[i]==s[i-1]) sum++;
        else if(sum>1){
            cnt++;
            e[cnt]=sum-1;
            sum=1;
        }
    }
    if(sum>1){
        cnt++;
        e[cnt]=sum-1;
    }
    int res=0;
    int ans=1;
    for(int i=0;i<=cnt;i++) {
        res+=e[i];
        ans=ans*(e[i]+1)%mod;
    }
    ans=ans*dfs(res)%mod;
    cout<<res<<' '<<ans<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

Problem - D - Codeforces

在看这题前,先看一题类似的异或序列 - 洛谷求所有区间的异或和相加

如果暴力枚举所有的区间的话,一定会超时

我们从贡献度的角度考虑,考虑每一位的贡献(从0到30位),先求这一位上的前缀异或和,一位的话要么为0,要么为1,要使得某个区间[l,r]对答案有贡献,那么该区间这一位的异或和得为1,这样乘上它所对应的权值才能对答案作出贡献,即a[r]^r[l-1]=1,(注意a数组为前缀异或和数组),由于只有0和1异或才能得1,所以当当前位为1时,产生的对答案有贡献的区间个数即为前面出现过的该位前缀异或和为0的个数,由于产生贡献的区间异或和都为1,所以当前位贡献即为产生贡献的区间个数再乘上对应的权值即可,同理,当当前位为0时,贡献即为前面出现过的该位前缀异或和位1的个数乘上对应的权值

注意初始化时cnt[0]得为1,因为求区间异或和为a[r]^a[l-1],a[0]为0,则初始就有一个0

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n;
void solve() {
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) a[i]^=a[i-1];
    int ans=0;
    for(int i=0;i<31;i++){
        int cnt[2]={1,0};
        for(int j=1;j<=n;j++){
            int now=((a[j]>>i)&1);
            ans+=cnt[now^1]*(1<<i);
            cnt[now]+=1;
        }
    }
    cout<<ans<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
//    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

接下来再看本题,就是在刚才的基础上改编一下,对于每一个区间都求区间异或和乘上区间长度,将所有区间的该值加起来

同样,从贡献度的角度考虑,考虑每一位的贡献,先求这一位上的前缀异或和,一位的话要么为0,要么为1,要使得某个区间[l,r]对答案有贡献,那么该区间这一位的异或和得为1,这样乘上该区间的长度再乘上对应的权值才能对答案作出贡献

当当前位为1时,我们需要用当前位置减去前面出现过的该位前缀异或和为0的位置,这样算出来的是对答案有贡献的一个区间的长度,然后将对前面出现过的所有该位前缀异或和为0的都这样操作,然后加起来,再乘上对应的权值,算出的就是当前位置产生的贡献,但是这样每次单独相减再相加太慢了,我们可以发现,假设当前位置为j,前面的满足的位置分别为x1,x2,x3,那么贡献即为(j-x1+j-x2+j-x3)*权值,我们完全可以化简为j*cnt-(x1+x2+x3),其中cnt为 前面出现过该位的前缀异或和为0的个数,x1+x2+x3为前面出现过的该位前缀异或和为0的位置之和,这些都是可以预处理的,更准确的说,是一边遍历一边处理

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=3e5+10,mod=998244353;
int a[N];
int n;
void solve() {
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) a[i]^=a[i-1];
    int ans=0;
    for(int i=0;i<31;i++){
        int cnt[2]={1,0},sum[2]={0};
        for(int j=1;j<=n;j++){
            int now=((a[j]>>i)&1);
            ans=(ans+(j*cnt[now^1]%mod-sum[now^1]%mod+mod)%mod*(1<<i))%mod;
            cnt[now]+=1;
            sum[now]+=j;
        }
    }
    cout<<ans<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
//    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值