Codeforces Round # 673 (Div. 2) AE 문제 해결

Codeforces Round # 673 (Div. 2) AE 문제 해결

콘테스트 링크 : https://codeforces.com/contest/1417


물, 탐욕스러운 질문

문제는 길이 n의 시퀀스가 ​​주어지고 상한 k가 주어지면 원래 시퀀스의 각 숫자는 1과 k 사이이므로 이제 작업 할 때마다 현재 시퀀스에 숫자를 추가 할 수 있습니다. 다른 숫자로, 값이 k를 초과하도록 할 수 없습니다. 이제 최대 몇 번까지 작동 할 수 있는지 물어보십시오.

가장 작은 값을 가진 것을 직접 선택하고이 가장 작은 값으로 가능한 한 많은 다른 숫자를 추가하십시오.

#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;
        int ans=0,tar=0;
        vector<int>num(n);
        for(int i=0;i<n;i++)
        {
    
    
            cin>>num[i];
            if(num[i]<num[tar]) tar=i;
        }
        for(int i=0;i<n;i++)
            if(i!=tar) ans+=(k-num[i])/num[tar];
        cout<<ans<<endl;
    }
} 

질문 B
욕심, 구조

문제는 길이 n의 시퀀스가 ​​주어지고 값 T가 주어지면 이제 원래 시퀀스를 두 시퀀스로 나눌 필요가 있다는 것을 의미합니다. 동일한 숫자 시퀀스에서 T까지 더해지는 두 시퀀스의 숫자는 첨자가 가장 적습니다.

탐욕 스러워요 T의 절반을 첫 번째 시리즈에 넣고 T보다 큰 절반을 두 번째 시리즈에 넣으십시오 .T의 절반은 두 개 미만의 시리즈로 균등하게 나눌 수 있습니다. T보다 작은 반은 T보다 큰 반에 더해 T와 같기 때문에 간단히 두 부분으로 나눌 수 있으며 균등 분포의 반과 정확히 같은 결론은 원고 용지에서 계산할 수 있습니다.

#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--)
    {
    
    
        ll n,T;
        cin>>n>>T;
        bool flag=0;
        vector<bool>color(n);
        for(int i=0;i<n;i++)
        {
    
    
            ll x;
            cin>>x;
            if(x*2==T) {
    
    color[i]=flag;flag=!flag;}
            else if(x*2<T) color[i]=0;
            else color[i]=1;
        }
        for(int i=0;i<n;i++) cout<<color[i]<<' ';
        cout<<endl;
    }
}

C 문제
문자열, 작은 결론,지도의 간단한 사용

길이 n의 시퀀스가 ​​주어지면 값 1-n, 시퀀스에있는 길이 k의 쌍과 모든 연속 하위 시퀀스, 공통적으로 가장 작은 수의 쌍과 k 값을 출력해야합니다. 공유 번호가 없으면 -1이 출력됩니다.

정면에서 직접 생각하면 생각하기 힘들 것입니다. 특정 값에서 볼 수 있습니다.이 값이 특정 길이 k의 가장 작은 공통 값으로 사용되는 경우 길이 k는 값이 시퀀스에 나타나는 모든 위치에서 인접한 위치 사이의 최대 거리보다 작지 않아야합니다 (참고 : 시퀀스의 가장 왼쪽과 가장 오른쪽이 특별히 심사됩니다.) 이 결론을 이용하여 map을 사용하여 for 루프의 각 값에 해당하는 최소 k를 구할 수 있습니다. 기록 후 kfor에 대한 1-n을 답으로 직접 수행하고 최소 k를 출력합니다.

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#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;
        map<int,pair<int,int>>M;//M[i]=pair{j,k},代表值为i的数字最后出现在下标j的位置,值为i的数字之间的最大间隔为k
        for(int i=1;i<=n;i++)
        {
    
    
            int x;
            cin>>x;
            if(M.find(x)==M.end()) M[x]=make_pair(i,i);//数列左侧特判
            else
            {
    
    
                M[x].second=max(M[x].second,i-M[x].first);
                M[x].first=i;
            }
        }
        vector<int>ans(n+1,INF);
        for(auto &x:M)
        {
    
    
            x.second.second=max(x.second.second,n+1-x.second.first);//特判数列右侧
            ans[x.second.second]=min(ans[x.second.second],x.first);
        }
        int now=INF;
        for(int i=1;i<=n;i++)
        {
    
    
            now=min(now,ans[i]);
            if(now==INF) cout<<-1<<' ';
            else cout<<now<<' ';
        }
        cout<<endl;
    }
}

질문 D
구조, 생각

질문은 길이 n의 시퀀스가 ​​주어지면 각 숫자의 값은 1과 1e5 사이의 양의 정수이며 이제이 시퀀스의 각 숫자 값을 최대 3n 연산으로 만들어야합니다. 같은.
각 연산에 ​​대해 두 개의 첨자 i와 j를 선택하고 0과 1e9 사이의 자연수 x를 선택하고 ai 에서 i × \ times를 뺄 수 있습니다.× x, aj에 i× \ times추가× x。

먼저, 전체 시퀀스의 합은 연산 중에 변경되지 않았으므로 전체 시퀀스의 합은 시퀀스 길이 n의 정수로 구성되어야합니다. 그리고 여기서 연산의 특수성은 두 개의 첨자의 값에 아래 첨자 i를 곱해야한다는 것입니다. 아래 첨자 1의 수가 충분히 크면 한 번의 연산으로 만족시킬 수 있다고 생각하기 쉽습니다. 위치를 구성하기 위해 먼저 전체 시퀀스의 모든 값을 어떤 식 으로든 a1에 수집 한 다음 n에 의해 할당 된 모든 위치에 대해 수집 할 수 있습니까? aj 위치의 모든 값을 a1로 이동하려면 aj 값을 j로 나눌 수 있어야합니다. 그런 다음 aj 값이 j를 나눌 수 없으면 다른 위치의 값을 aj에 더해야합니다.
a1을 사용하여 aj에 추가하여 aj를 j로 나누는 것을 생각하는 것은 매우 쉽습니다. 그러면 우리는 2에서 n까지 직접 a2에서 an으로 지우기 작업을 완료해야합니다. 원래 시퀀스의 모든 숫자는 이는 양의 정수이며, 이는 처음 n 개의 숫자의 합이 최소한 n이라는 것을 의미하며, 이는 aj의 값이 j로 나눌 수 있어야 함을 의미합니다.
따라서 아래 첨자 2에서 n까지의 첫 번째 패스, 두 작업의 각주기, 첫 번째 작업은 a1의 값을 aj에 추가하여 aj를 j로 나눌 수 있도록하고 두 번째 작업은 aj의 모든 값을 a1로 이동합니다. 의 위에.
이러한 작업 라운드는 2n-2 번 소비되고 전체 시퀀스의 모든 값이 a1로 이동 한 다음 아래 첨자에 대해 2에서 n으로 다시 이동되고 값은 a1에서 구성으로 직접 이동할 수 있습니다. 총 3n-3 개의 작업이 소비되고 구조가 완료되어야합니다.

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

ll num[maxn];
ll n,sum;

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        sum=0;
        cin>>n;
        for(ll i=1;i<=n;i++) {
    
    cin>>num[i];sum+=num[i];}
        if(sum%n) cout<<-1<<endl;
        else
        {
    
    
            sum/=n;
            cout<<3*(n-1)<<endl;
            for(ll i=2;i<=n;i++)//第一遍for把除了num[1]外的所有值清零,移动到num[1]上
            {
    
    
                ll temp=i-num[i]%i;
                if(temp==i) temp=0;
                cout<<1<<' '<<i<<' '<<temp<<endl;
                num[1]-=temp;
                num[i]+=temp;
                cout<<i<<' '<<1<<' '<<num[i]/i<<endl;
                num[1]+=num[i];
            }
            for(ll i=2;i<=n;i++) cout<<1<<' '<<i<<' '<<sum<<endl;//第二遍for直接利用num[1]构造
        }
    }
}

E 제목
작업, DFS 분할 및 정복, 결론

문제는 자연수 만 포함하는 길이 n의 숫자 시퀀스가 ​​주어지면 적절한 값을 찾아야합니다. 그래야 원래 숫자 시퀀스의 각 값이 XOR 처리되어 역순 인 새 숫자 시퀀스를 얻을 수 있습니다. 숫자 num이 가장 작고 가장 작은 숫자와 해당 출력 값이 출력됩니다.

여기서 우리는 0과 1의 시퀀스 만 있다고 가정하면, for 루프의 과정에서 0과 1이 몇 번이나 나타나는지 기록 할 수 있으며, 두 값에 따라 0과 1로 끝나는 역 쌍의 수를 형성 할 수 있습니다. 크기 차이는 XOR에 0 또는 1을 사용할지 여부를 결정합니다.
그때부터
우리는 높은 것에서 낮은 것까지 봅니다. 값에 따라 수열은 깊이 2의 거듭 제곱에 해당하는 여러 세트로 나뉩니다. 다른 세트의 경우, 그들 사이의 역순 쌍의 수가 이미 있습니다. 이전 레이어 번호 프로세스는 위 단락의 계산 프로세스에 따라 얻을 수 있으며 후속 영향은 하위 수준에만 영향을 미치고 현재 세트 간의 관계에는 영향을 미치지 않기 때문에 각 세트간에 영향을 미치지 않습니다.
여기를 누른 후 dfs 나누기 및 정복을 작성하십시오.

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

int n;
ll ans=0,out=0;//ans为最终的数列中逆序对的数量,out为对原数列中每个值进行异或的值

void dfs(vector<vector<ll>>now,int deep)//二维数组now记录数列在当前层数被分割成了几个部分,deep为当前算的是二进制第几位
{
    
    
    ll temp=1ll<<deep;
    ll cas0,cas1,sum0=0,sum1=0;//sum0和sum1分别记录当前二进制deep位上,以该位为0的值作为逆序对的右侧(也就是out在此位取0)的逆序对数量
    //和以该位为1的值作为逆序对的右侧(也就是out在此位取1,使得该位的所有数0和1转化)的逆序对数量
    vector<vector<ll>>next;//下一次dfs的二维数组
    vector<ll>next0,next1;//当前分割部分的vector中,二进制deep位为0和1的数字分别放入两个vector
    for(int i=0;i<now.size();i++)
    {
    
    
        cas0=cas1=0;
        for(int j=0;j<now[i].size();j++)
        {
    
    
            if(now[i][j]&temp) {
    
    sum1+=cas0;cas1++;next1.push_back(now[i][j]);}
            else {
    
    sum0+=cas1;cas0++;next0.push_back(now[i][j]);}
        }
        if(next0.size()) {
    
    next.push_back(next0);next0.clear();}//避免不需要的递归和空间浪费,如果next0和next1不为0才压入下次dfs的数组里
        if(next1.size()) {
    
    next.push_back(next1);next1.clear();}
    }
    if(sum0>sum1) {
    
    ans+=sum1;out+=temp;}//如果以该位为0的值作为逆序对右侧的逆序对数量大于以该位为1的值作为逆序对的右侧,代表out需要在该位取1
    else ans+=sum0;
    if(deep==0) return;
    dfs(next,deep-1);
}

int32_t main()
{
    
    
    IOS;
    cin>>n;
    vector<vector<ll>>num(1);
    num[0].resize(n);
    for(auto &x:num[0]) cin>>x;
    dfs(num,30);//dfs30层即可,1e9的最高位也不过2的30次
    cout<<ans<<' '<<out<<endl;
}

추천

출처blog.csdn.net/StandNotAlone/article/details/108913241