在这里插入图片描述

闲聊

感觉这场Div3比以往的都要有难度,感觉跟Div2差不多了要,依稀记得之前觉得Div3还是能AK的,现在做E都感觉比较吃力了,这次D还因为一个点没想通wa了几发

题解部分

A. Dr. TC

给定一个01串s,然后有n个字符串t,第i个字符串t分别是s复制过来之后将第i个字符反转,即将0变为1,将1变为0,问你这n个字符串总共有多少个1

算一遍就好,还是挺好算的
代码如下:

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;



int a[N];

void solve() {
   
    init();

    int n;
    cin>>n;
    string s;
    cin>>s;

    int num1=0,num2=0;
    for(int i=0;i<n;i++) {
   
        if(s[i]=='1') num1++;
        else num2++;
    }
    cout<<(n - 1 ) * num1 + num2<<"\n";
}

signed main() {
   
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);

    //for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";

    int t=1;
    cin>>t;
    while(t--) {
   
        solve();
    }
    return 0;
}

B.St. Chroma
给定一个n和一个x,要求构造一个0-n-1的排列,使得根据前i个数的mex值组成的数组中,x出现次数最大

涉及mex值的一般贪心就好,本题也是一样,只要贪心,要让mex值达到x,然后再尽可能让mex值不变就好,所以我们首先从0到x-1,然后把比x大的数输出一遍,最后输出x即可

代码如下:

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;


int a[N];

void solve() {
   
    init();

    int n,x;
    cin>>n>>x;
    for(int i=0;i<x;i++) cout<<i<<" ";
    for(int i=n-1;i>=x;i--) cout<<i<<" ";
    cout<<"\n";
}

signed main() {
   
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);

    //for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";

    int t=1;
    cin>>t;
    while(t--) {
   
        solve();
    }
    return 0;
}

C.Cherry Bomb

先给定n和x,然后给定一个长度为n的数组a和数组b,其中数组b有些数字丢失了(表示为 b i b_i bi = -1),问你有多少个数组b使得对于所有的i, b i b_i bi ≥0 并且 b i b_i bi≤x,并且 a i a_i ai + b i b_i bi 的值是相等的。

很简单,如果已经b数组已经有一部分数字,那么我们只要检查有无冲突便可,如果有冲突就是-1,如果没有冲突就是1。

至于b数组里的数字全部丢失的情况,我们想象一个数组平移的过程,在这个平移过程中,保证最小值大于等于0,保证最大值小于等于k便可,那么有多少个我们是可以直接计算得到的
(这边提倡自己推公式看看捏)

代码如下:

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;

int a[N];
int b[N];

void solve() {
   
    init();

    int sign = 1;
    int num = -1;

    int n,k;
    cin>>n>>k;
    int minvalue = MOD;
    int maxvalue = 0;

    for(int i=1;i<=n;i++) cin>>a[i],minvalue = min(minvalue,a[i]),
    								maxvalue =max(maxvalue,a[i]);

    for(int i=1;i<=n;i++) {
   
        cin>>b[i];
        //cout<<a[i]<<" "<<b[i]<<" "<<num<<"\n";
        if(b[i]!=-1) {
   
            if(num == -1) {
   
                num = a[i] + b[i];
            }
            else {
   
                if(a[i] + b[i] != num) {
   
                    sign = 0;
                }
            }
        }
    }

    if(!sign) {
   
        cout<<0<<"\n";
        return ;
    }
    //cout<<"num:"<<num<<"\n";

    if(num != -1) {
   
        for(int i=1;i<=n;i++) {
   
            if(num - a[i] < 0 || num - a[i] > k) {
   
                cout<<0<<"\n";
                return;
            }
        }
    }


    if(num != -1) {
   
        cout<<1<<"\n";
    }
    else cout<<k - (maxvalue - minvalue) + 1<<"\n";
}

signed main() {
   
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);

    //for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";

    int t=1;
    cin>>t;
    while(t--) {
   
        solve();
    }
    return 0;
}

D. Flower Boy
给定一个长度为n的数组a,一个长度为m的数组b,对a数组从左到右看,如果a数组里面的数字满足当前b数组第一个数字,那么拿走a数组当前数字以及b数组的第一个数字。现在能进行一次操作在任何位置插入一个任何值x,问你这个x最小是多少能够保证b数组里的所有数都能拿走,如果无论怎么操作都拿不走b数组所有数字,则输出-1,如果不需要操作就能拿完b数组里的所有数,那么就输出0

分析一下:如果遍历一遍发现不用操作也能拿完那就直接输出就好
如果需要操作,就想想我们肯定是让操作***来的数字和b数组某个位置对应,然后让a数组后面的数字和b数字后面的数字匹配

所以能想到一个暴力的做法:遍历b数组里的所有数字,表示操作***来的数字和b数组第i个数匹配,然后再遍历检查a数组后面的数字和b数字后面的数字,看是否能拿完b数组的所有数字

但是这种做法肯定是超时了的,所以我们就想想该如何优化,其实有经验的应该早就想到了,能够优化的就是遍历检查的这部分,应该优化成能够直接查询知道后面这部分是否能拿完b数组的所有数

所以我们可以开一个后缀数组,从后往前对a数组和b数组进行匹配检查,并且记录第i个数字能和b数组从后往前能拿b数组从后往前多少个数字

那我们还可以开一个前缀数组,从前往后对a数组和b数组进行同样的操作。最后我们遍历位置,查询哪个位置能够满足插入一个数字之后能够拿完b数组里的所有数字的,如果有那就记录最小值,如果没有就输出-1

代码如下:

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;

int a[N];
int b[N];
int pre[N];
int bac[N];

void solve() {
   
    init();

    int sign = 0;
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];

    for(int j=1;j<=m;j++) cin>>b[j];

    for(int i=1;i<=n;i++) {
   
        pre[i] = pre[i-1];
        if(pre[i] < m && a[i] >= b[pre[i] + 1]) pre[i]++;
        //cout<<i<<" "<<pre[i]<<"\n";
    }

    if(pre[n] == m) {
   
        cout<<0<<"\n";
        return ;
    }

    bac[n + 1] = 0;
    for(int i=n;i>=1;i--) {
   
        bac[i] = bac[i + 1];
        if(bac[i] < m && a[i] >= b[m - bac[i]]) bac[i]++;
        //cout<<i<<" "<<bac[i]<<"\n";
    }

    int minvalue = MOD;

    for(int i=1;i<=n+1;i++) {
   
        if(pre[i-1] + bac[i] + 1 >= m) {
   
            sign = 1;
            minvalue = min(minvalue,b[pre[i-1] + 1]);
        }
    }
    if(sign) cout<<minvalue<<"\n";
    else cout<<-1<<"\n";
}

signed main() {
   
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);

    //for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";

    int t=1;
    cin>>t;
    while(t--) {
   
        solve();
    }
    return 0;
}

E.Wolf

给定n和q,然后给定一个长度为n的排列。在接下来q次查询中,每次查询给定l,r,k.然后会在[l,r]范围进行二分操作,二分操作详情如下:
在这里插入图片描述
由于p排列并不一定是排好序的,所以有可能二分操作找不到想要的数字,但是你可以选择d个非目标数字(d的大小由自己定),然后对其任意排序。问你d的大小最小为多少能够让该二分操作能够找到下想要的数字

那我们只要记录每个数字的位置,然后跟着二分的过程贪心就好了。具体来讲就是如果目标数字在右边,那么我们这里就需要一个比目标数字小的数字,反之同理

记录额外需要比目标数字小的位置个数,以及额外需要比目标数字大的位置个数,那么我们要移走全部,选定的肯定是这两个数字的最大值乘2

代码如下:

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;

int a[N];
int b[N];
int pos[N];

void solve() {
   
    init();

    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n;i++) {
   
        cin>>a[i];
        pos[a[i]] = i;
    }

    while(q--) {
   
        int l,r,k;
        cin>>l>>r>>k;

        if(pos[k] < l || pos[k] > r) {
   
            cout<<-1<<" ";
            continue;
        }

        int havebig=0,havesmall=0;
        int needbig=0,needsmall=0;

        while(l < r) {
   
            int mid = (l + r) >>1;
            if(pos[k] == mid) break;

            if(pos[k] > mid) {
   
                if(a[mid] < k) havesmall++;
                else needsmall++;
                l = mid + 1;
            }
            else {
   
                if(a[mid] > k) havebig++;
                else needbig++;
                r = mid - 1;
            }

        }

        //cout<<"\n"<<havebig<<" "<<needbig<<" "<<havesmall<<" "<<needsmall<<":";
        if(havebig + needbig > n - k  || havesmall + needsmall > k - 1) cout<<-1<<" ";
        else cout<<max(needbig,needsmall) * 2<<" ";
    }
    cout<<"\n";
}

signed main() {
   
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);

    //for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";

    int t=1;
    cin>>t;
    while(t--) {
   
        solve();
    }
    return 0;
}