Codeforces Round 1003 Div.4(A-H,除F题)

好难的一场Div.4,本来想着随便打,然后比赛开始的时候还没到家,在路上开了G和C题
太困了没写完,明天继续

A. Skibidus and Amog’u

题意:把一个字符串最后的ususus改为iii

输入前|S|-2个字符,再输出一个iii

void solve() {
    string s;cin>>s;
    for(int i = 0;i<s.size()-2;i++){
        cout<<s[i];
    }
    cout<<"i"<<endl;
}

B. Skibidus and Ohio

题意:给出一个字符串,可以执行任意次操作,每次操作可以将两个相邻且相同的字符替换为任意字符,求该字符串可以达到的最小长度

只要一个字符串可以执行一次操作,那么将其替换成前一个或后一个字符就可以一直操作下去直到变为1,否则长度不变

void solve() {
    string s;cin>>s;
    for(int i = s.size()-1;i>0;i--){
        if(s[i] == s[i-1]){
            cout<<1<<endl;
            return;
        }
    }
    cout<<s.size()<<endl;
}

C2. Skibidus and Fanum Tax (hard version)

题意:有a和b两个数组,长度分别为n和m,对于a中的每个数都可以执行仅1次如下操作:在m中选择一个数字,将aia_iai变为bj−aib_j-a_ibjai,求是否可以将数组A变为非递减排序

C1我没看,应该是不用二分直接暴力就可以,就不写了

首先,贪心的想,从前往后遍历数组a,如果可以将其变为一个更小的数,那么一定不将其变为较大的数,那么首先先将第一个数变为可以达到的最小值为min(a[0]−b[0],a[0])min(a[0]-b[0],a[0])min(a[0]b[0],a[0]),b[0]表示b中最小的数,然后去遍历a中的每个数,将其变为比a[i-1]大的最小值,具体操作为:b[j]−a[i]>=a[i−1]b[j]-a[i] >=a[i-1]b[j]a[i]>=a[i1]可以写为b[j]>=a[i−1]+a[i]b[j]>=a[i-1]+a[i]b[j]>=a[i1]+a[i],不等式后面的值是确定的,所以我们要做的是在数组b中找到第一个大于等于a[i−1]+a[i]a[i-1]+a[i]a[i1]+a[i]的值,直接用二分查找找这样的一个值就行,然后有四种情况,代码写的很乱,就是(不)需要修改和(不)能找到满足的b[j]b[j]b[j],这四种情况,然后判断一下就行。我用的mi去表示当前处理过的最小值

void solve() {
    int n,m;cin>>n>>m;
    vector<int> a(n),b(m);
    for(int i = 0;i<n;i++){
        cin>>a[i];
    }
    for(int i = 0;i<m;i++){
        cin>>b[i];
    }
    sort(b.begin(),b.end());
    auto check = [&](int x){
        int l = 0,r = m-1;
        while(l<r){
            int mid = (l+r)>>1;
            if(b[mid]>=x)   r = mid;
            else   l = mid+1;
        }
        if(b[l]<x)  return -1;
        else    return l;
    };
    int mi = min(a[0],b[0]-a[0]);
    for(int i = 1;i<n;i++){
        int ans = mi+a[i];
        int tmp = check(ans);
        if(tmp == -1)   ans = INT_MIN;
        else    ans = b[tmp]-a[i];
        if(ans<mi && a[i]<mi){
            cout<<"NO"<<endl;
            return;
        }
        else{
            if(ans<mi){
                mi = a[i];
            }
            else if(a[i]<mi){
                mi = ans;
            }
            else{
                mi = min(ans,a[i]);
            }
        }
    }
    cout<<"YES"<<endl;
}

D. Skibidus and Sigma

题意:给你n个长度为m的数组,将这n个数组按照任意顺序连接成一个长度为n∗mn*mnm的数组b,使得这个数组的分数∑i=1n∗m∑j=1ibj\sum_{i = 1}^{n*m}\sum_{j = 1}^{i}b_ji=1nmj=1ibj的值最大

看到按照任意顺序想到是一个排序问题,那么应该怎么排才能使得答案最大呢?
可以想到每个数对分数的贡献为ai∗(∣a∣−i+1)a_i*(|a|-i+1)ai(ai+1)∣a∣|a|a表示数组a的长度,那么把大的数排在前面一定是没错的,但由于只能对n个数组排序,不能对每个数组的内部元素进行排序,所以应该是尽可能总和大的数组排在前面是更优的,那么又万一两个数组元素不同但总和相同,这时候应该如何排。

其实到这里举了好多例子也没发现有啥规律,后来想到按照排序的做法想一下

对于数组A和B,设其长度均为n,和表示为sumA,sumBsum_A,sum_BsumA,sumB,计算一下AB拼接和BA拼接的分数
cnt(AB)=∑i=1n(2n−i+1)∗ai+∑i=1n(n−i+1)bi cnt(AB) = \sum_{i = 1}^{n}(2n-i+1)*a_i+\sum_{i = 1}^n(n-i+1)b_i cnt(AB)=i=1n(2ni+1)ai+i=1n(ni+1)bi
提一个naina_inai出来,即
cnt(AB)=∑i=1nn∗ai+∑i=1n(n−i+1)∗ai+∑i=1n(n−i+1)bicnt(AB)=n∑i=1nai+∑i=1n(n−i+1)∗ai+∑i=1n(n−i+1)bi cnt(AB) = \sum_{i = 1}^{n}n*a_i+\sum_{i = 1}^{n}(n-i+1)*a_i+\sum_{i = 1}^n(n-i+1)b_i\\ cnt(AB) = n\sum_{i = 1}^{n}a_i+\sum_{i = 1}^{n}(n-i+1)*a_i+\sum_{i = 1}^n(n-i+1)b_i cnt(AB)=i=1nnai+i=1n(ni+1)ai+i=1n(ni+1)bicnt(AB)=ni=1nai+i=1n(ni+1)ai+i=1n(ni+1)bi
观察一下可以发现n∑i=1nain\sum_{i = 1}^{n}a_ini=1nai就是sumasum_asuma,而后面两项分别为A,B单独时的分数

然后再同理计算一下cnt(BA)=n∑i=1nbi+∑i=1n(n−i+1)bi+∑i=1n(n−i+1)∗aicnt(BA) = n\sum_{i =1}^nb_i+\sum_{i = 1}^n(n-i+1)b_i+\sum_{i = 1}^{n}(n-i+1)*a_icnt(BA)=ni=1nbi+i=1n(ni+1)bi+i=1n(ni+1)ai

可以发现,其实只和放在前面的数组的和有关,和内部元素是什么样的没关系,解决了刚刚想的,万一有两个数组和一样怎么办的疑问,所以排序方式即为按照总和的大小从大到小去链接即可。

void solve() {
    int n,m;cin>>n>>m;
    vector<vector<i64>> a(n,vector<i64>(m));
    for(int i = 0;i<n;i++){
        i64 sum = 0;
        for(int j = 0;j<m;j++){
            cin>>a[i][j];
            sum+=a[i][j];
        }
        a[i].pb(sum);
    }
    sort(a.begin(),a.end(),[](vector<i64> a,vector<i64>b){
        return a.back()>b.back();
    });
    i64 ans = 0;
    i64 pre = 0;
    for(int i = 0;i<n;i++){
        for(int j = 0;j<m;j++){
            pre+=a[i][j];
            ans+=pre;
        }
    }
    cout<<ans<<endl;
}

E. Skibidus and Rizz

题意:将一个01字符串的平衡值定义为∣0的个数−1的个数∣|0的个数-1的个数|∣0的个数1的个数,给出n表示0的个数,m表示1的个数,用这些01组成一个字符串使得其所有子串的平衡值最大为k,如果不存在这样的字符串,输出-1

很简单可以想到,平衡值最大的情况为所有1放一边,所有0放一边,此时子串中的平衡值最大为max(n,m)max(n,m)max(n,m),所以当k>max(n,m)k>max(n,m)k>max(n,m)时,不存在满足题意的字符串

然后因为所有0和1都要用上,所以不论怎么构造,最终字符串的平衡值为∣n−m∣|n-m|nm,即如果k<∣n−m∣k<|n-m|k<nm的话,无论怎么构造也不会出现满足题意得字符出串。

构造方式为:

假设n<m,先放k个1,之后01交替地放置字符,直到某个字符个数为0停止,此时剩余的字符个数在放置的时候计算一下,然后在最后将剩余的字符全部放在末尾。

在m>n的时候,按照n<m的方式构造字符串,最后再将01反值即可

void solve() {
    int n,m,k;
    cin>>n>>m>>k;
    if(k>max(n,m) || k<abs(n-m)){
        cout<<"-1"<<endl;
        return;
    }
    int f = 0;
    if(n>m){
        f = 1;swap(n,m);
    } 
    vector<char> a;
    for(int i = 0;i<k;i++){
        a.pb('1');
        m--;
    }
    int tmp = 0;
    while(n && m){
        if(tmp == 0){
            a.pb('0');
            tmp = 1;n--;
        }
        else{
            a.pb('1');
            m--;tmp = 0;
        }
    }
    if(n>k || m>k){
        cout<<"-1"<<endl;
        return;
    }
    if(n){
        while(n){
            a.pb('0');
            n--;
        }
    }
    else{
        while(m){
            a.pb('1');
            m--;
        }
    }
    for(int i = 0;i<a.size();i++){
        if(f == 1){
            if(a[i] == '0') cout<<'1';
            else    cout<<'0';
        }
        else{
            cout<<a[i];
        }
    }
    cout<<endl;
}

G.Skibidus and Capping

题意:给出一个数列,求其中有多少组数的lcmlcmlcm为半素数,半素数为有且仅有两个质因子

这个题想了太久了,当时看题的时候还在路上,也不记得最开始是怎么一步步想的了

其实我觉得是一个分类题,对于一个半素数,哪些数的lcmlcmlcm为半素数呢。

首先两个质数的lcmlcmlcm一定为半素数,此时lcm(a,b)=a∗blcm(a,b) = a*blcm(a,b)=ab,但注意两个数不能相同,质数不可能是半素数
然后如果a为半素数,那么找到一个b,使得lcm(a,b)=alcm(a,b) = alcm(a,b)=a,也为半素数,这里的b可以等于a,也可以为a的两个因子中的一个

按照上面说的,要找的数只和素数和半素数有关,所以数列中其他的数可以直接扔掉了。

由于数据范围中aia_iai的值不大,所以可以直接先预处理出2∗1052*10^52105以内的素数和半素数,记录下来给出的数列中哪些数是素数哪些是半素数,再去看能够组合出多少对即可。还是因为素数和半素数不可能相同,所以不用考虑重复的问题。

1、找两个不相等的质数的组合数,例如用a1,a2,..,axa_1,a_2,..,a_xa1,a2,..,ax表示数列中不同质数的数量,其和为sumasum_asuma,之后求∑i=1nai(sum−prei)\sum_{i = 1}^{n}a_i(sum-pre_i)i=1nai(sumprei)preipre_iprei表示a的前缀

2、找两个相等的半素数,注意可以是下标相同的数,所以对于有x个半素数a,对答案的贡献为(x+1)∗x/2(x+1)*x/2(x+1)x/2

3、找一个半素数和其因数的组合,可以在预处理的时候顺便将每个半素数的两个因数也处理出来,再直接加上因数的个数乘对应半素数的个数即可

const int N = 200000;
vector<i64> prime;
map<i64, pair<int,int>> semi;
bool vis[N+10];
int has[N+10];
void euler_sieve(){
    for(int i = 2;i<=N;i++){
        if(!vis[i]){
            prime.pb(i);
            has[i] = 1;
        } 
        for(int j = 0;j<prime.size();j++){
            if(i*prime[j]>N)    break;
            vis[i*prime[j]] = 1;
            if(i%prime[j] == 0) break;
        }
    }
}
void semiprime(){
    for (int i = 0; i < prime.size(); i++){
        for (int j = i; j < prime.size(); j++) {
            i64 x = prime[i] * prime[j];
            if (x >= N) break;
            semi[x] = {prime[i], prime[j]};
        }
    }
};
void solve() {
    int ans = 0;
    int n;cin>>n;
    vector<int> a(n);
    int ma = 0;
    for(int i = 0;i<n;i++){
        cin>>a[i];
        ma = max(ma,a[i]);
    }
    map<int,int> mp1;
    for(int i = 0;i<n;i++){
        if(has[a[i]])   mp1[a[i]]++;
    }
    map<array<int,3>,int> mp2;
    for(int i = 0;i<n;i++){
        if(semi[a[i]].fi){
            if(has[semi[a[i]].fi] || has[semi[a[i]].se]){
                mp2[{a[i],semi[a[i]].fi,semi[a[i]].se}]++;
            }
        }
    }
    for(auto &[x,y]:mp2){
        ans+=mp1[x[1]]*y;
        ans+=mp1[x[2]]*y;
        if(x[1] == x[2]){
            ans-=mp1[x[2]]*y;
        }
        if(y){
            ans+=(1+y)*y/2;
        }
    }
    int sum = 0;
    for(auto &[x,y]:mp1){
        sum+=y;
    }
    for(auto &[x,y]:mp1){
        sum-=y;
        ans+=sum*y;
    }
    cout<<ans<<endl;
}
signed main(){
    cin.tie(0)->ios_base::sync_with_stdio(0);
    int t = 1;
    cin>>t;
    euler_sieve();
    semiprime();
    while(t--){
        solve();
    }
    return 0;
}

H.Bro Thinks He’s Him

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using u128 = unsigned __int128;
#define endl "\n"
#define pii pair<int,int>
#define pIi pair<i64,int>
#define piI pair<int,i64>
#define PII pair<i64,i64>
#define pb push_back
#define fi first
#define se second

const int mod = 998244353;
const int N = 200005;

i64 fp(i64 a, int n) {
	i64 ans = 1;
	a %= mod;
	while (n) {
		if (n & 1)	ans = (ans * a) % mod;
		a = (a * a) % mod;
		n >>= 1;
	}
	return ans;
}

struct Seg {
    vector<i64> tree;
    int size;
    Seg(int n) {
        size = 1;
        while (size < n) size <<= 1;
        tree.assign(2 * size, 0);
    }
    void add(int pos, i64 val) {
        pos += size - 1;
        tree[pos] = ((tree[pos] + val)+mod)%mod;
        for (pos >>= 1; pos >= 1; pos >>= 1) {
            tree[pos] = ((tree[pos << 1] + tree[(pos << 1) + 1])+mod)%mod;
        }
    }
    i64 query(int l, int r) {
        l += size - 1;
        r += size - 1;
        i64 res = 0;
        while (l <= r) {
            if (l % 2 == 1) res = ((res + tree[l++])+mod)%mod;
            if (r % 2 == 0) res = ((res + tree[r--])+mod)%mod;
            l >>= 1;
            r >>= 1;
        }
        return res;
    }
};

void solve(){
    string str;
    string s;
    int m;
    cin >> str >> m;
    i64 n = str.size();
    s = string(n + 1, '0');
    i64 res = ((fp(2, n) - 1)+mod)%mod;
    Seg sum0(n), sum1(n);
    auto work = [&](int idx){
        i64 l1,r1,l0,r0,ans,f;
        if(idx>1){
            l1 = sum0.query(1,idx-1);
        }
        else{
            l1 = 0;
        }
        if(idx<n){
            r1 = sum1.query(idx+1,n);
        }
        else{
            r1 = 0;
        }
        l0 = ((fp(2, idx-1) - 1 - l1)+mod)%mod;
        r0 = ((fp(2, n - idx) - 1 - r1)+mod)%mod;
        ans = ((l0 + r0 + 2 * l0 * r0 - l1 - r1 - 2 * l1 * r1)+mod)%mod;
        if(s[idx] == '0'){
            f = 1;
        }
        else{
            f = -1;
        }
        res = ((res + f * ans)+mod)%mod;
        sum0.add(idx, f * fp(2, idx-1));
        sum1.add(idx, f * fp(2, n - idx));
        s[idx] = s[idx]^1;
    };
    for (int i = 0; i < n; ++i) {
        if (str[i] == '1') {
            work(i + 1);
        }
    }
    while (m--) {
        int idx;
        cin >> idx;
        work(idx);
        cout << (res+mod)%mod << ' ';
    }
    cout<<endl;
}
signed main() {
    cin.tie(0)->ios_base::sync_with_stdio(0);
    int t = 1;
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值