Codeforces Round #669 (Div. 2) AD problem solution

Codeforces Round #669 (Div. 2) AD problem solution
// Written in rating value 1987/2184
// AC was slow in this field, D did not appear, rating value -46

In this game, Question A thought about a magical method, and it was also complicated to write. It took 16 minutes to pass. Question B had a normal speed of 10 minutes, and the interactive question of Question C imposed a meaning (asked? xy must satisfy x<y), and then I thought about a strange algorithm. After wa dropped, reread the question and found that it passed without this condition. It took about 40 minutes to write. When doing the D question, the idea of ​​dp and the policy of seeking the dp transfer path were introduced, but the specific logical relationship was not clarified, and the A and C questions somewhat affected the mentality.
Poor state may be a reason, but you can discover your own shortcomings and learn from the failure.

Contest link: https://codeforces.ml/contest/1407 Question
A
Classification discussion, greedy

The title means to give you an even-numbered sequence of length n that only contains 0 and 1. You can delete at most n/2 digits in it, so that in the final sequence, 1 on the even subscript and 1 on the odd subscript The number of 1 is the same.

The simple idea here is that if we finally construct a number sequence that contains only 0 or only 1, and the length is an even number, it must meet the conditions.

Record the number of occurrences of 0 in the original sequence, num0, and the number of occurrences of 1 in num1.
When num0>=num1, num1 must be less than or equal to n/2, and we delete all 1, leaving a sequence of all 0s. Even and odd numbers in length meet the requirements.
When num0<num1, num0<n/2, we delete all 0, and there is at least one chance to delete. At this time, there is a sequence of all 1s, and the length must be an even number to satisfy the condition. Therefore, if num1 is an odd number, we delete a 1 again.

#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 num0=0,num1=0;
        for(int i=0;i<n;i++)
        {
    
    
            int x;
            cin>>x;
            if(x) num1++;
            else num0++;
        }
        if(num0>=num1)//如果0的个数大于等于1的个数,代表1的个数小于等于n/2,删光1直接输出全0数列即可
        {
    
    
            cout<<num0<<endl;
            for(ll i=0;i<num0;i++)
            {
    
    
                if(i) cout<<' ';
                cout<<0;
            }
        }
        else
        {
    
    
            if(num1&1) num1--;//如果1的个数大于0的个数,0的个数小于n/2,删光0,如果1的个数是奇数,则再删一个1,变为全1数列
            cout<<num1<<endl;
            for(ll i=0;i<num1;i++)
            {
    
    
                if(i) cout<<' ';
                cout<<1;
            }
        }
        cout<<endl;
    }
}

Question B:
structure, greed, violence

The question means that given n numbers, you need to rearrange them in order to satisfy the maximum lexicographic order of the c[] array generated by the sequence of numbers. c[i] is equal to the gcd of the first i digits in the arranged array. It is sufficient to output any one of a variety of structural sequences that meet the conditions.

Here, the value of n is relatively small. We can record the gcd of the currently constructed i numbers as temp, and then greedily find the largest value of temp's gcd among the remaining unused numbers.
We will find that there may be multiple maximum values ​​of gcd with temp, but we can continue to deduce that the order of these numbers will not be affected.
Because gcd(a,b)=c, c must be less than or equal to a and b.
Assuming that the maximum value of gcd(temp,x) is y, there are multiple corresponding x at this time, we choose any one of them, temp becomes y, and then look for the maximum value of gcd(y,x), at this time The maximum gcd is still y, and the satisfied number is still the number of the last search for gcd (temp, x). It follows that those numbers can be chosen arbitrarily.

Just follow this line of thought.

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

bool flag[1007];//flag[i]记录i这个数字是否已经被使用

int gcd(int a,int b)
{
    
    
    return b?gcd(b,a%b):a;
}

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n;
        cin>>n;
        vector<int>num(n);
        for(auto &x:num) cin>>x;
        memset(flag,0,sizeof(flag));
        vector<int>ans;//ans为我们最后构造的数列
        int tar=0;
        for(int i=0;i<n;i++)//找到最大的那个数字,放在最后构造数列的第一个位置
        {
    
    
            if(num[i]>num[tar])
            {
    
    
                tar=i;
            }
        }
        ans.push_back(num[tar]);
        flag[tar]=1;

        int temp=num[tar];//temp记录当前构造的ans数组中最后一个位置的值
        for(int i=1;i<n;i++)
        {
    
    
            int Max=0,tar=-1;//tar记录剩下未被使用的数字中,与temp的gcd值最大tar为多少
            for(int j=0;j<n;j++)
            {
    
    
                if(!flag[j])
                {
    
    
                    if(gcd(temp,num[j])>Max)//此处的tar有多个,取任意一个均可,原因见题解
                    {
    
    
                        Max=gcd(temp,num[j]);
                        tar=j;
                    }
                }
            }
            ans.push_back(num[tar]);
            flag[tar]=1;
            temp=gcd(temp,num[tar]);
        }

        for(int i=0;i<ans.size();i++)
        {
    
    
            if(i) cout<<' ';
            cout<<ans[i];
        }
        cout<<endl;
    }
}

Question C
Interaction, implementation, summary conclusion

The question means that there is a permutation num[] of length n. You can ask for the two subscripts x and y in each interaction, and the result of num[x]%num[y] will be returned to you. Now you need to get the specific value of each subscript position with at most 2n interactions.

First of all, we must think about what kind of query we can use to determine the value of a certain subscript position.
Note that if num[x]<num[y], the result returned to us is num[x], but the key is that when we asked, we didn't know which num[x] or num[y] was larger.
So what if we ask num[x]%num[y], exchange xy, and then loop the value of num[y]%num[x]?

Assuming num[x]%num[y]=c, num[y]%num[x]=d,
we consider the case of num[x]<num[y] (the case of num[x]>num[y] is the same Reason, no more details)
At this time c=num[x], and d must be less than num[x], so c>d.
That is to say, the larger number of our two queries is the smaller number of num[x] and num[y], in this case it is num[x], and x is the query that obtained c Therefore, we can determine the smaller number of num[x] and num[y] through two queries, and determine it with its corresponding subscript.

From this, we use tar to record the current undetermined maximum subscript, and directly for it again, query [tar,i] and [i,tar] all the way, determine the smaller of num[i] and num[tar], and update tar subscript, just apply.

#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=1e4+7;

int num[maxn];
int n;

int32_t main()
{
    
    
    IOS;
    cin>>n;
    int tar=1;//tar指示当前扫过部分的最大值的下标,for循环时该位置的值是不确定的
    for(int i=2;i<=n;i++)
    {
    
    
        int x,y;
        cout<<"? "<<tar<<' '<<i<<endl;cout.flush();
        cin>>x;
        cout<<"? "<<i<<' '<<tar<<endl;cout.flush();
        cin>>y;
        if(x>y)//代表x是num[tar]和num[i]中较小的那个,并且x=num[tar]
        {
    
    
            num[tar]=x;//确定下标tar的值为x
            tar=i;//更新不确定的最大值位置下标为i
        }
        else num[i]=y;//num[tar]>num[i]时,不需要更新tar,直接确定下标i的值为y
    }
    num[tar]=n;//for结束后还剩下最后一个tar位置'不确定',但是他是这n排列中最大的,自然只能是n了
    cout<<"! ";
    for(int i=1;i<=n;i++)
    {
    
    
        if(i>1) cout<<' ';
        cout<<num[i];
    }
    cout<<endl;
}

Question D
dp, dp transfer path needs to be preprocessed, and the relational logic needs to be derived first

The title means that there are n tall buildings, and you start from the first building to the nth building. You can move to a tall building next to each other, or jump to a tall building on the right at once to meet the height of the tall building in the middle of this section, all of which are higher or lower than the height of the start and end points. Ask you the minimum number of operations required.

This question is easy to think of using dp to transfer. Violent dp is definitely not enough, and then think about whether it can handle all the transfer paths of the four situations that the question intends and then dp.
What I didn’t clarify here during the competition is this situation. For this kind of data:
4 1 2 3 5
4 can jump directly to 2 or directly to 3, all satisfying that the height of the middle part is lower than the height of the starting point and the ending point. Yes, I couldn’t figure out how to deal with it efficiently.

But in fact, 4 is the first number on the left of 2 that is not less than 2, and it is also the first number on the left of 3 that is not less than 3.

Here we can use stack processing in the forward for direction to get the first subscript on the left side of the current position that is not less than/greater than the current position, and then use stack processing in the reverse direction to get the first one on the right side of the current position that is not less than/greater than the current position The subscript of the position can include all transitions, and also include the transitions that move to the adjacent position. (Here combined with the above data to understand and understand)

After preprocessing the dp transfer path, follow these paths to transfer...Here, my code does not preprocess the number of transfer locations for each location like the official problem solution, but directly uses the four preprocessed conditions above The four arrays are transferred.

#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=3e5+7;

ll n;
ll h[maxn];
ll dp[maxn];//dp[i]记录从下标0位置开始到下标i位置最少需要多少次跳跃
ll r_higher[maxn];//r_higher[i]记录在下标i的右侧,第一个不小于h[i]的下标位置,指示dp转移方向i到r_higher[i]
ll r_lower[maxn];//r_lower[i]记录在下标i的右侧,第一个不大于h[i]的下标位置,指示dp转移方向i到r_lower[i]
ll l_higher[maxn];//l_higher[i]记录在下标i的左侧,第一个不小于h[i]的下标位置,指示dp转移方向从l_higher[i]到i
ll l_lower[maxn];//l_lower[i]记录在下标i的左侧,第一个不大于h[i]的下标位置,指示dp转移方向从l_lower[i]到i

int32_t main()
{
    
    
    IOS;
    cin>>n;
    for(ll i=0;i<n;i++)
    {
    
    
        dp[i]=r_higher[i]=r_lower[i]=l_higher[i]=l_lower[i]=llINF;//初始化状态代表不存在
        cin>>h[i];
    }
    stack<ll>S;//定义一个栈实现我们后面分四种情况记录dp转移路径的过程
    //只需要能够理解其中一种,就能理解其他三种
    for(ll i=0;i<n;i++)//求l_higher[],也就是左侧的第一个不小于h[i]的位置下标
    {
    
    
        while(S.size()&&h[S.top()]<h[i]) S.pop();//这里我们把栈中记录的小于h[i]的位置全部弹出
        if(S.size()) l_higher[i]=S.top();//如果栈中还有剩余,那么栈顶的位置就是最近的一个位置下标
        S.push(i);//把当前位置推入栈中,在栈中已存在的下标中不存在比当前的h[i]更小的h值(关键)
    }

    while(S.size()) S.pop();
    for(ll i=0;i<n;i++)
    {
    
    
        while(S.size()&&h[S.top()]>h[i]) S.pop();
        if(S.size()) l_lower[i]=S.top();
        S.push(i);
    }

    while(S.size()) S.pop();
    for(ll i=n-1;i>=0;i--)
    {
    
    
        while(S.size()&&h[S.top()]<h[i]) S.pop();
        if(S.size()) r_higher[i]=S.top();
        S.push(i);
    }

    while(S.size()) S.pop();
    for(ll i=n-1;i>=0;i--)
    {
    
    
        while(S.size()&&h[S.top()]>h[i]) S.pop();
        if(S.size()) r_lower[i]=S.top();
        S.push(i);
    }

    dp[0]=0;
    for(ll i=0;i<n;i++)
    {
    
    
        if(l_lower[i]!=llINF) dp[i]=min(dp[i],dp[l_lower[i]]+1);//这里dp转移的时候一定要先转移l_的两个部分
        if(l_higher[i]!=llINF) dp[i]=min(dp[i],dp[l_higher[i]]+1);//因为是更前面的位置转移到当前位置,要比下面两种r_过程的当前位置转移到更后面要优先
        if(r_lower[i]!=llINF) dp[r_lower[i]]=min(dp[r_lower[i]],dp[i]+1);
        if(r_higher[i]!=llINF) dp[r_higher[i]]=min(dp[r_higher[i]],dp[i]+1);
    }

    cout<<dp[n-1]<<endl;
}

Guess you like

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