Codeforces Global Round 10 A-E题解

Codeforces Global Round 10 AE problem solution
// Written in the large rating value of 2075/2184, the small rating value of 1887/1887
//I just went home these few days, and I have another game. I will train before the end of the month, maybe Then dove down

Question A
Simple thinking

Given a number sequence of length n containing only positive integers , you can select two adjacent numbers with unequal values ​​for each operation, delete them and then put their sum back to the original position, that is to say, every time After this operation, the length of the sequence will be reduced by one.
Now you need to output the shortest sequence length you can finally get.

First of all, if there is only one number in the entire sequence, then we have no way to do any operation on this sequence, and only output the original sequence length n.
When the sequence contains more than two numbers, we assume that at the beginning, the largest number in the sequence is X. Since there are other numbers in the sequence besides X, there must be an X, and its adjacent positions are not A number equal to X.
For the first time, we combine this X with the adjacent number that is not equal to X, and we get a new value Y that is greater than X. That is to say, there is no longer a number equal to Y in the sequence. For the operation, use this largest number to combine with other numbers each time, and the length of the array must be changed to 1.

Therefore, directly judge whether there is only one number in the original sequence, if there is only one number, output n, otherwise output 1.

#include<bits/stdc++.h>
#define ll long long
#define int long long
#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;
        vector<int>num(n);
        for(auto &x:num) cin>>x;
        bool flag=1;
        for(int i=0;i+1<n;i++)
            if(num[i]!=num[i+1]) flag=0;
        if(flag) cout<<n<<endl;
        else cout<<1<<endl;
    }
}

Question B
Simple thinking

Given a sequence of numbers (negative numbers may exist), you need to output k operations, what is each value in the sequence.
In each operation, the maximum value in the current sequence is d, and the value of each number in this sequence is changed to d minus the original value.

Observe the second set of samples
. There are negative numbers in the original number sequence, and the maximum value is 5.

After the first operation, the original sequence becomes
0 6 1 3 5

It can be noticed that the original negative number has become a positive number, and the negative number with the largest absolute value has become the current maximum value, and the original maximum value of 5 has become 0.
In other words, after the first operation, we will definitely get a 0, and all negative numbers will disappear.

The current number sequence is a number sequence containing only positive numbers and 0. After the next few operations, the number sequence becomes
6 0 5 3 1
0 6 1 3 5
6 0 5 3 1
0 6 1 3 5

It is easy to notice that this is a form of two operations as a loop. The reason is that if the maximum value of the current sequence is d, after each operation, the 0 in the original sequence will become d (because there is no negative number in the sequence, d is the maximum value at this time), and the d in the original sequence will be Becomes 0. Then the next time the operation is performed, the maximum value in the sequence is still d, and there is still 0.
It is easy to observe that this has constituted a condition for two operations and one cycle.

The operand k>=1, so we directly perform an operation on the original sequence, remove the negative number and get a 0. After that, since it is a cycle of two operations, it is enough to decide whether to perform another operation according to whether k is an even number.

#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;

void change(vector<ll> &num)
{
    
    
    ll M=-llINF;
    for(auto x:num) M=max(M,x);
    for(auto &x:num) x=M-x;
}

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        ll n,k;
        cin>>n>>k;
        vector<ll>num(n);
        for(auto &x:num) cin>>x;
        change(num);
        if(k==1)
        {
    
    
            for(auto x:num) cout<<x<<' ';
            cout<<endl;
        }
        else
        {
    
    
            if(k%2==0) change(num);
            for(auto &x:num) cout<<x<<' ';
            cout<<endl;
        }
    }
}

Question C
Greedy, implement

Given a sequence of numbers, you can select a continuous area for each operation, add 1 to each value in this area, and ask you how many operations you need at least to make the entire sequence of numbers without decreasing values.

Here you can understand the following example

For the sequence 3 1 2 1 5, if
we regard the value of this sequence as height, it is easy to find that there are two "bottoms", two 1s at position 2 and position 4.
If we operate these two valleys separately: After
the first operation: 3 2 2 1 5 After the
second operation: 3 3 3 1 5
At this time, the first valley has been "filled"
after the third operation: 3 3 3 2 5 After the
fourth operation: 3 3 3 3 5
This requires 4 operations.

However, the optimal solution is the following: After
the first operation: 3 2 2 1 5 After the
second operation: 3 2 2 2 5 After the
third operation: 3 3 3 3 5

For the first valley, the largest number that appears before is 3, so at least 3-1=2 operations are required to fill it out. Similarly, the largest number that appears before the second valley is 3, which also requires 2 operations. To fill it.
But as demonstrated by the optimal solution, for the second trough, the original number sequence is 3 1 2 1 5, and the 2 at position 3 is the first peak on the left. We only need to fill in 1 at position 4 to 2 "Catch a ride to fill the bottom of the valley ahead."

This leads to a conclusion. For each valley, we just need to add up, and the value of the first peak to the left minus the value of the current valley is the final answer.

#include<bits/stdc++.h>
#define ll long long
#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--)
    {
    
    
        ll n;
        cin>>n;
        vector<ll>num(n);
        for(auto &x:num) cin>>x;
        ll ans=0;
        ll M=num[0],cas=num[0];//M记录当前位置左侧第一个峰顶的值为多少,cas为当前的谷底的值为多少
        for(ll i=1;i<n;i++)
        {
    
    
            if(num[i]>=num[i-1])
            {
    
    
                ans+=M-cas;
                M=cas=num[i];
            }
            else cas=num[i];
        }
        ans+=M-cas;
        cout<<ans<<endl;
    }
}

Question D
Summary of laws, greedy

N people stand in a circle, and everyone will attack the person on their left or right.
There is a special rule that if a person is attacked by only one person, then the person he attacked must also be the person who attacked him. There are no restrictions on who is attacked by 0 or 2 people.

Now given the initial attack mode of n individuals, asking at least a few people need to change the attack mode, so that the above special regulations can be met.

To summarize the observation sample, we can list the basic situations on the manuscript paper:
(At this time, the circle is not considered, the right person on the right is not the first person)
L
R
LL
RR
LR
LRR
LLR
LLRR
These are all It is to meet a special fixed situation.
It is easy to prove that all other situations are not satisfied.
So let's summarize the above several situations, in fact, it contains all the situations where there are no more than 2 consecutive characters.

From this, we can turn the problem into that, at least through a few operations, the original string can be made that there are no consecutive characters with a length greater than or equal to 3 that are the same.

For the original string, there are two kinds of characters originally, we directly scan out the length of the same area of ​​each character in the for, and at each position of length 3 we put a character different from the current area character to construct aims. Therefore, divide the length by 3 and add it to the answer.
Pay special attention to this string is cyclic, so we need to deal with the initial situation, the first and last characters are equal.

In addition, when the original string contains only one kind of character, we insert a different character at any position, then the original string becomes two areas of length 1 and n-1, and the final answer is the first Add (n-1)/3 to one operation.

#include<bits/stdc++.h>
#define ll long long
#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;
        vector<char>S(n);
        for(auto &x:S) cin>>x;
        bool f=1;
        for(int i=1;i<n;i++) if(S[i]!=S[i-1]) f=0;
        if(f) cout<<1+(n-1)/3<<endl;
        else
        {
    
    
            ll ans=0,cas=1;//cas代表当前扫到的连续的相同字符个数
            while(S[0]==S[n-1]){
    
    cas++;n--;}//处理头和尾相同的情况
            for(ll i=1;i<n;i++)
            {
    
    
                if(S[i]!=S[i-1])//如果当前位置与上一个位置不同,代表当前连续相同区域结束
                {
    
    
                    ans+=cas/3;
                    cas=1;
                }
                else cas++;
            }
            cout<<ans+cas/3<<endl;
        }
    }
}

Question E
interaction, dp, observation data, construction

Interactive questions (what if a good interactive question is two points???)
Give you a value n (maximum 25), you need to construct a n × \times× n matrix, each grid is a value in the range of [0,1e16].
After that, you will be given k values, starting from (1,1) for k, you can only go right or down until you reach the sum of all values ​​on the path of (n,n).

You are required to restore these k paths.

First of all, it is easy to think of, using the 26 base, the first line is all constructed with 26 0 =1, the second line is fully constructed with 26 1 =26, and the third line is fully constructed with 26 2 =676...
Use each corresponding number to represent the first How many squares have we walked to the right in a few rows.
But this kind of scheme is not feasible, because it has a limit on the size of the value we construct, the maximum cannot exceed 1e16, in fact, we can write a simple program to check it, and we can find this kind of construction scheme, the value we need is far Exceeded the upper limit of longlong.

Then think about it, we must construct a matrix so that the values ​​obtained by each path are different, we don't care how to construct it. Let's first think about how to restore the original path from this value. One idea is to record the prefix sum of each position in each row, and dfs violently enumerates each row. We walked a few steps to the right, but in fact this solution is inevitable It will be t. Because we can write a simple dp to calculate 25 × \timesThe number of schemes for the × 25 matrix, if I remember correctly, should be 1e15, and this complexity is dfs to the end of the world.
In fact, we have discovered through the dp array that the number of solutions is very close to the upper limit of the numerical size limit of each grid structure given by the title. And this value is very large, we must use an O(1) or O(logn) way to get the choice of going right or down at each position.

What I think of here is a lexicographic scheme. When we walk from (1,1) to (n,n), we must go n-1 times to the right, and n-1 times. We list the path directions one by one, as a string, and artificially stipulate the character ' The lower'is greater than the character'right'.
So it's easy to get it. Going from (1,1) all the way to the right and going down to the end is the smallest value solution. We construct all 0s for the first row and last column, so that the value of this path is 0.
Next is the process of constructing other paths:
For any position (i, j), we consider the path from (1, 1) to (1, j) and then to (i, j), and there is no path after that Restrictions, it is easy to prove that this method of thinking includes all paths.
After we walk from (1,1) to (1,j) and then to (i,j), since we are constructed according to the above-mentioned lexicographic order, all the schemes that go from (i,j) to the right are Less than going down.
For the position (i,j), in the scheme of going right, going right to the head and then going down to the top is the smallest value, and going one square to the right and then going down to the head and then going right to the head is the largest value of.

We construct from the n-1th column to the left, and for each column from top to bottom.
We use an x ​​to record the minimum value after walking from (1,1) to (1,j) and then to (i,j), and then we can calculate the current position value through the path of the previous paragraph. (Both above and right have been constructed).
And the transfer of x, from the position (i, j) to the position (i+1, j), (think about the lexicographic order above, for the position (i, j), the value of the right-going scheme is small And the number of path plans that go down), that is, the total number of paths (n, n) after (i, j) goes to the (i, j+1) position to the right. This can be obtained by preprocessing the dp array O(1).

After the construction is completed, we can use the dir array, dir[i][j] to record starting from (i,j), go one square to the right, and then walk to the maximum value of (n,n) at will. (Actually, this path is to go one square to the right, go down to the end, and then go to the end to the right)
If the path value given to us by the question is less than or equal to the maximum value of going right, it means that our plan is included in going right In the case of, the converse is included in going down to the situation.
From this, we can already do O(1) to get the direction at each position.

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

ll n;
ll sum;
ll field[30][30];//存储我们构建的矩阵每个位置的值,我们构造的矩阵的所有路线,对于field[i][j]来说,向下走的任意方案,一定比向右走的所有方案得到的值都更大
//以此来得到dir数组,做到O(1)指示我们的路径方向
ll dir[30][30];//dir[i][j]代表从当前位置向右走再到终点,可以获得的最大值,用来指示我们走到(i,j)时应该向下还是向右走
//实际上这个路线是先往后走一步,再向下走到底,再向右走到底
ll Data[30][30];//Data[i][j]代表从(i,j)走到(i,n)得到的值,就是一个反向的前缀和,辅助计算
ll dp[30][30];//dp[i][j]存储从(1,1)走到(i,j)有几种方案

void getdp()
{
    
    
    dp[1][0]=1;
    for(ll i=1;i<=25;i++)
    {
    
    
        for(ll j=1;j<=25;j++)
        {
    
    
            dp[i][j]=dp[i-1][j]+dp[i][j-1];
        }
    }
}

void getfield()
{
    
    
    for(ll i=1;i<=n;i++) field[1][i]=field[i][n]=0;//构造第一行和最后一列为0
    ll x=1;//x为对于(i,j)位置来说,从(1,1)先向右走到(1,j),再向下走到(i,j),再向右的最小值,即继续向右走到底再向下走到底
    for(ll j=n-1;j;j--)
    {
    
    
        ll temp=0;//temp记录当前field[i][j]的上方所有值的和
        for(ll i=2;i<=n;i++)
        {
    
    
            //见x代表的值
            //第一行和最后一列我们构造的全为0,因此只需要让x减去field[i][j]上方和右方的累加值即为field[i][j]应当构造的值
            field[i][j]=x-temp-Data[i][j+1];
            temp+=field[i][j];//累加竖直上方的前缀和
            Data[i][j]=field[i][j]+Data[i][j+1];//累加右方的前缀和
            x+=dp[n-i+1][n-j];//对于field[i][j]来说,它第一步向右走,再走到终点的情况即为[n-i+1,n-j]的矩阵的方案数
            //那么对于field[i+1][j]来说,它向右走的最小值就应该比field[i][j]的最小值加上对应的dp值方案数
        }
    }
}

void getdir()
{
    
    
    for(ll i=1;i<=n;i++)
    {
    
    
        for(ll j=1;j<n;j++)
        {
    
    
            for(ll k=i;k<=n;k++)
                dir[i][j]+=field[k][j+1];
            for(ll k=j+2;k<=n;k++)
                dir[i][j]+=field[n][k];
            dir[i][j]+=field[i][j];
        }
    }
}

int32_t main()
{
    
    
    IOS;
    cin>>n;
    getdp();
    getfield();
    getdir();
    for(ll i=1;i<=n;i++)
    {
    
    
        for(ll j=1;j<=n;j++)
            cout<<field[i][j]<<' ';
        cout<<endl;
    }
    ll k;
    cin>>k;
    while(k--)
    {
    
    
        cin>>sum;
        ll nowr=1,nowc=1;//nowr为我们当前所在行,nowc为我们当前所在列
        while(nowr<=n&&nowc<=n)
        {
    
    
            cout<<nowr<<' '<<nowc<<endl;
            if(dir[nowr][nowc]<sum||nowc==n) {
    
    sum-=field[nowr][nowc];nowr++;}
            //如果我们当前的值,比向右走得到的最大值都大的话,代表当前方案应该在下方
            else {
    
    sum-=field[nowr][nowc];nowc++;}
            //如果我们当前的值,比向右走得到的最大值要小或等于,代表当前方案应该在右侧
        }
        cout<<endl;
    }
}

Guess you like

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