Codeforces Round #812 (Div. 2) A -D

A.Traveling Salesman Problem

最短路径即矩形缺一角,我们将那一角做平移,即可得一个矩形,矩形周长即最短路径

#include <bits/stdc++.h>

#define eps 1e-8    //多2
#define inf 0x3f3f3f3f
#define PI acos(-1)     //π
#define for(l,r) for(int i = l; i <= r; i++)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
using namespace std;

typedef long long ll;

int TEST;               //测试案例数
const int N = 1e5+5;    //数组长度

int n,m;
int x[200],y[200];
void solve()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    int xl=0,xr=0,yl=0,yr=0;
    for(0,n-1){

    cin >> x[i] >> y[i];
    xl = min(x[i],xl);
    xr=max(x[i],xr);
    yl=min(yl,y[i]);
    yr=max(y[i],yr);
    }
    cout << 2*(yr+xr-yl-xl) << endl;
}

int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> TEST;
    while(TEST--)   
        solve();
    return 0;
}

B.Optimal Reduction

只要中间没有凹点即可,反过来说,只要排列单调增、单调减或者先增后减都是满足的

#include <bits/stdc++.h>

#define eps 1e-8    //多2
#define inf 0x3f3f3f3f
#define PI acos(-1)     //π
#define for(l,r) for(ll i = l; i <= r; i++)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
using namespace std;

typedef long long ll;

int TEST;               //测试案例数
const int N = 1e5+5;    //数组长度

int n;
int q[N];
void solve()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    int minx=0;
    for(0,n-1)
        cin >> q[i];
    int ind=0;
    if(n == 1)      //长度为1,直接输出YES
        YES;
    if(n >= 2 && q[0] > q[1])  //长度大于1且开始是递减,则之后只能递减,若有递增,则输出NO
    {
        for(0,n-2){
        if(q[i] >= q[i+1])
            ind = i+1;
        else
            break;
        }
        if(ind == n-1)
            YES;
        NO;
    }
    for(0,n-2){     //长度大于1且开始是递增,之后可以一直递增,或者某一时刻出现递减后并持续递减
        if(q[i] <= q[i+1])
            ind = i+1;  //找到递增的结束点(可能是递减的开始点)
        else
            break;
    }
    if(ind == n-1)  //递增序列
        YES;
    for(ind,n-2)    //若运行至此,则为非递增序列,判断是否满足上述条件,即出现递减后一直递减
    {
        if(q[i] < q[i+1])   //若再次出现递增,则输出NO
            NO;
    }
    YES;    //满足先增后减条件
}

int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> TEST;
    while(TEST--)   
        solve();
    return 0;
}

C.Build Permutation

首先,根据以下本人描述的做法,可以证明n个元素一定可以组成某个排列满足本题描述条件。
f(n)表示求出0~n-1这n个元素满足题意的排列,并存入ans[1]到ans[n]中。
易知:
f(1): 0
f(2): 1 0
f(3): 1 0 2
f(4): 0 3 2 1
f(5): 4 3 2 1 0

根据递归来做,假设f(n)可求出n个元素满足条件的排列。即求0~n-1满足条件的排列,对于n-1,我们对其开方得到整数x,判断 x 2 x^{2} x2与n-1的大小,若相等,则说明n-1为完全平方数,我们之间将答案赋值为n-1,n-2,… ,0,则据题意依次加上0,1,…,n-1后,每一项都是n-1(即 x 2 x^{2} x2)这个完全平方数。
若n-1大于 x 2 x^{2} x2(不会出现小于的情况),则将x加1,此时 x 2 x^{2} x2一定大于n-1,则我们向下调用 f( x 2 x^{2} x2-(n-1)),并将答案数组的第( x 2 x^{2} x2-n+2)项至最第n项依次赋值为 x 2 x^{2} x2-i+1(i为项数),则这些项每一项都是 x 2 x^{2} x2这个完全平方数。
依次如此递归处理下去。
最后给出退出条件,即n=1与n=2时,给答案数组赋值。为了加快速度,可以给出更多的退出条件,如n=3,4…

#include <bits/stdc++.h>

#define eps 1e-8    //多2
#define inf 0x3f3f3f3f
#define PI acos(-1)     //π
#define for(l,r) for(ll i = l; i <= r; i++)
using namespace std;

typedef long long ll;

int TEST;               //测试案例数
const int N = 1e5+5;    //数组长度

int n;
int ans[N];

void f(int n)
{
    if(n == 1){
        ans[1]=0;
        return;
    }  
    if(n == 2)
    {
        ans[1]=1;
        ans[2]=0;
        return;
    }  
    int x = int(sqrt(n-1));
    
    if(x*x == n-1){
        for(1,n)ans[i]=n-i;
        return;
    }if(x*x < n-1) x++;
    int next = x*x-n+1;
    for(next+1,n) ans[i] = x*x-i+1;
    f(next);
}
void solve()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    f(n);
    for(1,n)cout << ans[i] << " ";
    cout << endl;
}

int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> TEST;
    while(TEST--)   
        solve();
    return 0;
}

D.Tournament Countdown

分析:

n=1时,询问一次直接得出答案
n>=2时,以四个人为一组,从最底层往上,每按序从左向右取四个人,1和2比,3和4比,分别有一胜一负,假设输的为2、3,则2和3赢的次数一样多,而1、4进行比赛,假设赢的是1,则1至少比4多赢一场(设2、3分别赢i场,则4赢了i+1场,由于如果比赛到此不结束,那么1可以继续参加后续比赛,故1至少赢i+2场)。所以每从左往右取四个人(不论是最底层还是非最底层),一定有两个人赢的场数相等(第一轮淘汰的人),剩下两人,一人比第一轮淘汰的人多赢一场,另一人比第一轮淘汰的人至少多赢两场。
例如n=3的一种情况:
在这里插入图片描述

策略:

每次选四个人,编号为1、2、3、4 。做两次询问得出这四个人中赢的最多的人:
(假设四人到目前赢的场数均为i,则1和2中必定有一人赢的场数停留在i,3、4同理)
询问1,3:
若1赢的多,则说明2在第一轮和1比赛时被淘汰,若3赢的多,则说明4在第一轮和3比赛时被淘汰。
不妨假设1赢的多,那我们再询问1,4:
若1赢的多,则说明4人中1赢的最多,即只有1能够进入之后的比赛,此时我们将1对应的选手存入数组。同理,若4赢的多,则赢的场数:4>1>3=2,4进入之后比赛,将4对应选手存储。

依次处理 2 n 2^{n} 2n个选手,我们又得到了一个大小为 2 n − 2 2^{n-2} 2n2的数组,更新n并重复上述操作,直到n<=1时退出,此时仅余两位选手(特殊情况:当初始n=1时,一次处理后,n=0,仅余一位选手,直接对其输出),询问一次得出结果。

可行性分析

在这里插入图片描述

实现:
#include <bits/stdc++.h>

#define eps 1e-8    //多2
#define inf 0x3f3f3f3f
#define PI acos(-1)     //π
#define for(l,r) for(ll i = l; i <= r; i++)
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
using namespace std;

typedef long long ll;

int TEST;               //测试案例数
const int N = 2e5+5;    //数组长度

int n;

int winner;
vector<int> q;
vector<int> tmp;
int ww[N];
int ask(int a, int b)
{
    cout << "? " << a << " " << b << endl;
    int read;
    cin >> read;
    return read;
}
int my_power(int x)
{
    return (1 << x);
}
void solve()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    vector <int>().swap(q); 
    vector <int>().swap(tmp); 
    cin >> n;
    int length = my_power(n);
    if(n==1)
    {
        winner = ask(1,2);
        cout << "! " << winner << endl; 
        return;
    }
    
    tmp.push_back(0);
    for(1,length)
        tmp.push_back(i);   //初始化数组
    int ind = length+1;
    int ix = 1;
    int t;      //暂存返回值(0 1 2) 
    int length_this; 
    while(ind > 3)
    {
        q.swap(tmp);
        tmp.clear();
        tmp.push_back(0);
        ind = 1;
        ix = 1;
        length_this = my_power(n);
        while(ix <= length_this-3)
        {
            t = ask(q[ix],q[ix+3]);
            if(t == 0)
            {
                t=ask(q[ix+1],q[ix+2]);
                if(t == 1) tmp.push_back(q[ix+1]);
                else   tmp.push_back(q[ix+2]);
            }
            else if(t == 1)
            {
                t=ask(q[ix],q[ix+2]);
                if(t==1) tmp.push_back(q[ix]);
                else    tmp.push_back(q[ix+2]);
            }
            else
            {
                t = ask(q[ix+1],q[ix+3]);
                if(t==1)tmp.push_back(q[ix+1]);
                else tmp.push_back(q[ix+3]);
            }
            ind+=1;
            ix+=4;
        }
        n-=2;
    }
    if(n==0)
    {
        cout << "! " << tmp[1] << endl;
        return;
    }
    // cout << tmp[1] << "---" << tmp[2] << endl;;
    winner = ask(tmp[1],tmp[2])==1?tmp[1]:tmp[2];
    cout << "! " << winner << endl; 
    // cout.flush();
    return;
}

int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> TEST;
    while(TEST--)   
        solve();
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值