Codeforces Round 983 (Div. 2) A-D

A

思路

只需要考虑打开的开关,最少的个数要分奇偶讨论,如果是奇数那么最少个数是1,因为两个打开的开关可以互相抵消,最后会剩下一个,最多的个数就是看打开的开关个数和关闭的开关的个数,设打开的个数是 x x x,那么关闭的个数就是 2 n − x 2n-x 2nx,如果少于一半那么答案就是打开开关的个数,否则就是 n − ( x − n ) = 2 n − x n-(x-n)=2n-x n(xn)=2nx也就是关闭的开关个数

代码

#include 
#pragma gcc optimize(2)
using namespace std;
#define ll long long


int main(){
    int t;
    cin>>t;
    while(t--){
        int n,a=0,b=0;
        cin>>n;
        for(int i=1;i<=2*n;i++){
            int p;
            cin>>p;
            if(p==1) ++a;
            else ++b;
        }
        int a1,a2;
        if(a&1) a1=1;
        else a1=0;
        a2=min(a,b);
        cout<<a1<<' '<<a2<<endl;
    }
    return 0;
}

B

思路

可以把 k k k单独拿出来,那么 k k k左边有 k − 1 k-1 k1个元素,那么 k k k右边有 n − k n-k nk个元素,可以发现当 k k k为奇数时,左右元素个数都是偶数,那么把左右都分成两块就好了,当 k k k为奇数时,左右元素个数都是奇数,那么直接把左右元素都当成一个整体就行,特判一下n=1
Codeforces Round 983 (Div. 2) A-D_第1张图片

代码

#include 
#pragma gcc optimize(2)
using namespace std;
#define ll long long


int main(){
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        if(n==1){
            cout<<1<<endl;
            cout<<1<<endl;
        }
        else if(m&1){
            if(m==1||m==n) cout<<-1<<endl;
            else{
                cout<<5<<endl;
                cout<<1<<' '<<2<<' '<<m<<' '<<m+1<<' '<<m+2<<endl;
            }
        }
        else{
            cout<<3<<endl;
            cout<<1<<' '<<m<<' '<<m+1<<endl;
        }
    }
    return 0;
}

C

思路

满足最后情形的充要条件是最小两个元素之后大于最大的元素,那直接枚举最小和次小两个元素就行了,排个序,最小和次小一定是相邻的,二分找一下第一个比最小和次小之和大的位置,这个位置之后的所有元素都要进行缩小

代码

#include 
#pragma gcc optimize(2)
using namespace std;
#define ll long long
const int maxn=2e5+5;
int val[maxn];

int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) cin>>val[i];
        sort(val+1,val+1+n);
        int ans=0x3f3f3f3f;
        val[n+1]=0x7f7f7f7f;
        for(int i=1;i<=n-1;i++){
            int pos=n-(lower_bound(val+1,val+1+n+1,val[i]+val[i+1])-val-1);
            ans=min(ans,pos+i-1);
        }
        cout<<ans<<endl;
    }
    return 0;
}

D

思路

根据第一个条件我们可以发现,这颗树的形态是很多链和0节点相连,根据 x ≤ y x \leq y xy p x ≤ p y p_x \leq p_y pxpy,可以发现1的父亲是0,又因为1有俩邻居,所以第一个父亲不是0的节点的父亲一定是1,因为根据第一个条件除了0节点外都只有一个儿子,找到第父亲是1的节点 x x x后, 1 1 1 x x x的所有节点的父亲都是0, x x x的父亲是1,那么我们可以用set存一下叶子节点,从 x + 1 x+1 x+1开始每次遍历叶子节点,询问和叶子节点是否的路径是否经过0,如果不经过0,那么这个节点的父亲就是这个叶子节点,将小于这个叶子节点的节点都从set里删掉,并且将这个节点insert,因为这是新的叶子节点

代码

#include 
#pragma gcc optimize(2)
using namespace std;
#define ll long long
const int maxn = 1e4 + 5;
int p[maxn];

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        --n;
        for(int i=1;i<=n;i++) p[i]=-1;
        bool z = 0;
        int tot = 2;
        set<int> st;
        int pos;
        for(int i=2;i<=n;i++){
            cout << '?' << ' ' << 1 << ' ' << i << endl;
            int op;
            cin>>op;
            if(op==0){
                pos=i;
                break;
            }
        }
        for(int i=1;i<=pos-1;i++) p[i]=0;
        p[pos]=1;
        tot=pos+1;
        for(int i=2;i<=pos;i++) st.insert(i);
        while (tot <= n)
        {
            vector<int>v;
            v.clear();
            for (auto it : st)
            {
                cout << '?' << ' ' << it << ' ' << tot << endl;
                int q;
                cin >> q;
                v.push_back(it);
                if (q == 0)
                {
                    p[tot] = it;
                    st.insert(tot);
                    ++tot;
                    for(auto s:v) st.erase(s);
                    break;
                }
            }
        }
        cout << '!';
        for (int i = 1; i <= n; i++)
        {
            cout << ' ' << p[i];
        }
        cout << endl;
    }
    return 0;
}

E

待补…

你可能感兴趣的:(算法)