Educational Codeforces Round 96 (Rated for Div. 2)A-E题解

Educational Codeforces Round 96 (Rated for Div. 2) AE problem solution
Contest link: https://codeforces.com/contest/1430

Question A
Simple structure

The question means that the total number is now n. Can you use a number of 3, 5, and 7 to form the number n, and output any construction scheme.

Here, the result after taking the remainder of n%3 is divided into three cases for consideration.
When n%3=0, just construct n/3 3s directly.
When n%3=1, after constructing n/3 3s, there will be 1 remaining. We need at least 2 3s to form a 7 with this 1, so at this time n/3 must have at least 2 Just work.
When n%3=2, after constructing n/3 3s, there will be 1 remaining. We need at least 1 3 to form a 5 with this 2, so at this time n/3 must have at least 1 Just work.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n;
        cin>>n;
        int rest=n%3;
        if(rest==0) cout<<n/3<<' '<<0<<' '<<0<<endl;
        else if(rest==1)
        {
    
    
            if(n/3<2) cout<<-1<<endl;
            else cout<<n/3-2<<' '<<0<<' '<<1<<endl;
        }
        else
        {
    
    
            if(n/3) cout<<n/3-1<<' '<<1<<' '<<0<<endl;
            else cout<<-1<<endl;
        }
    }
}

Question B
Simple and Greedy

The question means that there are n buckets of water, each with a certain amount of water at the beginning. You can pour any amount of water from one bucket to another for each operation. After k operations, you will be asked for the maximum and minimum amounts of water. What is the maximum difference in the amount of water between the buckets?

Here we can directly take the greedy process.
After 1 operation, we can gather the largest 2 buckets of water in one bucket, and get an empty bucket with 0 water quantity.
After 2 operations, we can put the largest 3 buckets of water. Gathered in a bucket and got an empty bucket
with 0 water ...

So we directly sort and add the largest k+1 bucket (be careful not to exceed n) water together to be the answer.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n,k;
        cin>>n>>k;
        vector<ll>num(n);
        for(auto &x:num) cin>>x;
        sort(num.begin(),num.end());
        k=min(k+1,n);
        ll ans=0;
        for(ll i=1;i<=k;i++) ans+=num[n-i];
        cout<<ans<<endl;
    }
}

Question C
structure, greedy, small conclusion

The question means to give you a sequence of length n at the beginning, with numbers from 1 to n. You can select two numbers in the current sequence for each operation, add them up and divide by 2, round up and return to the original sequence. After n-1 operations, there is only one number left in the entire sequence.
Ask what is the smallest number, and construct an operation plan.

Here is a small conclusion.
First of all, if two numbers are added and divided by 2, the result is 1. Only when both numbers are 1.
If we use the number 1 we had at the beginning to construct it with other numbers, the current 1 will be used up and a number other than 1 will be constructed. We cannot construct 1 back.
And we don't use 1, and we can't construct another 1 with only other numbers, and we can't get two ones.
Therefore, the smallest value we get in the end cannot be 1, at least 2.
Next, prove that the final minimum is 2.
We directly construct from the current largest two numbers, at first we use n and n-1 to get n, then n and n-2 get n-1, n-1 and n-3 get n-2... until the last 3 and 1 get 2.
In this way, any sequence of n can be finally constructed to get 2, and it has been demonstrated that 1 is impossible to get.

From this, output the answer 2 directly, and then merge the two largest numbers each time.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n;
        cin>>n;
        cout<<2<<endl;
        cout<<n<<' '<<n-1<<endl;
        int now=n;
        while(now>2)
        {
    
    
            cout<<now<<' '<<now-2<<endl;
            now--;
        }
    }
}

Question D
Greedy, double pointer

The title means that given a string of length n that only contains 0 and 1, you have to select a character in the current string to delete each time, and then continue to delete all the characters with the same prefix in the remaining string . Ask how many times you can operate at most.

Here is the process of greedy directly.
In the second step of each operation, we have to delete all the characters with the same prefix, that is, the second step of all our operations will delete a continuous interval of the same characters, then the first step of all our operations is definitely You may not want to reduce the number of these intervals.
We use need to record the current number of characters to be deleted in the first step, and tar indicates the current position. For each operation need+1, the length of the same interval of the prefix currently scanned is L, then at most L-1 of them It can be used to delete need, that is, it is deleted in the first step during the previous and current operations.
The loop ends when the remaining length is less than need, or when it has been swept to the end.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int len;
        string s;
        cin>>len>>s;
        int need=0,tar=0,ans=0;//need指示当前位置后面需要删除多少个,tar为当前位置
        while(1)
        {
    
    
            ans++;
            int net=tar;
            while(net+1<len&&s[net+1]==s[tar]) net++;//找相同字符的前缀最右侧下标
            need++;//我们当前位置往后的地方在本次操作要再删一个数
            need=max(0,need-(net-tar));//当前前缀长度-1的数字,可以在前面的操作和当前的操作中贪心删掉
            if(len-net-1<=need||net==len-1) break;//剩余的部分如果比前面操作需要删除的数量多,或者已经扫到末尾了结束循环
            tar=net+1;
        }
        cout<<ans<<endl;
    }
}

Question E
Greedy, reverse order pair, bubbling model analogy

The question means that given a string containing only lowercase letters, you can only exchange characters in two adjacent positions per operation, and ask you how many operations you need to perform at least to make the string reverse.

First of all, for the same letter, the subscripts after flipping are known. We perform a greedy process. For the same letter, the subscripts in the original string correspond to the flipped subscripts from small to large. , Is the optimal situation.
Next, what we require is how many operations are required in this case. At this time, we already know the subscript position of each character in the reversed string under the optimal scheme.

Note that our operation is to exchange characters in two adjacent positions, then we can completely compare this process to bubble sorting. The minimum number of operations we need is the number of reverse pairs of these subscript positions, using a tree array Just beg.

#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll maxn=2e5+7;

vector<int>origin[26];
int revese[maxn];//reverse[i]记录翻转后的第i个位置,在原来的字符串中是第几个位置
int n;
string s;

int tree[maxn];
void add(int x,int v)
{
    
    
    for(;x<=n;x+=x&-x) tree[x]+=v;
}

int sum(int x)
{
    
    
    int ret=0;
    for(;x>0;x-=x&-x) ret+=tree[x];
    return ret;
}

int32_t main()
{
    
    
    IOS;
    cin>>n>>s;
    for(int i=0;i<n;i++) origin[s[i]-'a'].push_back(i);//26个字母记录从左往右的下标
    for(int i=0;i<26;i++)
    {
    
    
        int len=origin[i].size();
        for(int j=0;j<len;j++) revese[n-origin[i][len-j-1]]=origin[i][j]+1;//同一个字母,按照翻转后所在的位置,从左往右依次贪心放进去
        //由于要用树状数组,所以这里让下标变成从1开始
    }
    ll ans=0;
    for(int i=1;i<=n;i++)//利用树状数组求个逆序对,就是冒泡从原序列变成当前序列所需的最少操作次数了
    {
    
    
        ans+=i-1-sum(revese[i]);
        add(revese[i],1);
    }
    cout<<ans<<endl;
}

Guess you like

Origin blog.csdn.net/StandNotAlone/article/details/109080571