ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #812 (Div. 2)

2022-08-07 12:35:37  阅读:155  来源: 互联网

标签:Div int t2 交换 Codeforces t1 812 我们 define


Codeforces Round #812 (Div. 2)

D. Tournament Countdown

分析

头晕脑胀的,时间复杂度算错了。

我们只要发现,四个中询问两个就可以确定哪两个一定不是冠军

我们,对于四个返回的情况一个个讨论。为方便讨论,我们将四个询问的位置定位\(x_0,x_1,x_2,x_3\),并且假设询问的是\(x_0,x_3\)

1

则代表,\(x_0>x_3\)。

则,我们首先可以推断出来,\(x_3\)一定不会是赢家,\(x_0\)可能会是冠军。

其次,我们去考虑\(x_1,x_2\),因\(x_0\)可能会是赢家,因此\(x_1\)一定不会是冠军。

因此,我们可以一次去掉两个。

2

与1的情况同理,不多解释。

0

平的情况最好说了,\(x_0,x+3\)平局,则两者都不可能是冠军。

则我们就解决了这个问题。

AC_code

#include <bits/stdc++.h>
#define fi first    
#define se second    
#define endl '\n'
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 1e5 + 10,M = N*2;

int get(int a,int b)
{
    cout<<"? "<<a<<" "<<b<<endl;
    int x;cin>>x;
    return x;
}

void solve() {
  int n; cin >> n; n = 1 << n;
  vector<int> a(n);
  iota(a.begin(), a.end(), 1);
  int m = n;
  while(m >= 4) {
    m /= 2;
    vector<int> b(m);
    int l = 0, r = m - 1;
    for(int i = 0, j = m * 2 - 1; i < j; i += 2, j -= 2) 
    {
      int res = get(a[i], a[j]);
      if(res == 1) {
        b[l ++ ] = a[i];
        b[r -- ] = a[j - 1]; 
      } else if(res == 0) {
        b[l ++ ] = a[i + 1];
        b[r -- ] = a[j - 1]; 
      } else if(res == 2) {
        b[l ++ ] = a[i + 1];
        b[r -- ] = a[j]; 
      }
    }    
    a.swap(b);
  }

  int res = get(a[0], a[1]);
  if(res == 1) {
    cout << "! " << a[0] << endl;
  } else {
    cout << "! " << a[1] << endl;
  }
}
 
int main() 
{
    // ios;
    int T=1;
    cin>>T;
 
    while(T -- ) {
        solve();
    }
 
    return 0;
}

E. Cross Swapping

分析

首先,我们需要发现一个小规律。

对于,\(a_{ij}\)其所能换过去的地方只有\(a_{ji}\),其能交换的方法是将k选为ij

因为,我们想让字典序尽量小。因此我们一定是从左到右考虑,尽量让两个可以互相交换的位置,字典序小的地方值尽量小。

因此,我们现在来考虑,对于可以交换的两个位置的三种情况。

我们假设,t1=a[i][j],t2=a[j][i],(i<j)

t1<t2

我们对于当前的两个位置,一定是尽量不交换,或者交换两次,即我们要不交换,要不就是同时选k=ik=j才是最优的。

t1==t2

交不交换都ok啦。

t1>t2

我们对于当前的两个位置,一定是交换一次,即我们一定选择k=ik=j才是最优的。

如果只是简单换两个位置就简单了,但是,我们发现当我们想选择交换某两个位置时,其还会影响到其余的格子,这样就不保证是最小的字典序了。

比如,如果我们交换了a[2][3]a[3][2],我们选择的是k=2的换法,但是此时,a[2][1]a[1][2]也会发生交换,但是a[2][1]>a[1][2],并且其更靠前,因此不换才是最优的。

总的来说,我们字典序靠前的位置的选择情况,是会对后面选择产生影响的。

同时,我们观察到,其中的某些选择是,选择一个不选另一个,这不难想到用扩展域并查集。

扩展域并查集

我们用i+n来表示不选ii来表示选择i

则我们重新回看对于交换中的三种情况,

我们假设,t1=a[i][j],t2=a[j][i],(i<j)

  1. t1<t2

    则,我们可以将ij放到一个集合,将i+nj+n放到一个集合,这样表示要不不选,要选两个操作要一起进行。

  2. t1==t2

    则,我们不用进行任何操作。

  3. t1>t2

    则,我们可以将ij+n放到同一个集合,将ji+n放到同一个集合,这样表示两者不能同时选。

最后找到操作序列时,我们一次考虑k=1,2...n,因为我们想要字典序尽量小。

为什么用1?因为我们想要字典序最小,所有如果第一个操作是有意义的,那我们一定会从1开始。

这里有个小技巧,我们按顺序将所有可以做的操作集合全部合并到1所带领的操作集合,这样最后看k可以取的值,就是看是不是在1的操作集合中。

那,我们来看看代码。

AC_code

#include <bits/stdc++.h>
#define fi first    
#define se second    
#define endl '\n'
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 1e5 + 10,M = N*2;

int p[N<<1];

int find(int x)
{
    if(p[x]!=x) p[x] = find(p[x])   ;
    return p[x];
}

void merge(int a,int b)
{
    int pa = find(a),pb = find(b);
    if(pa!=pb) p[pa] = pb;
}

void solve() 
{
    int n;cin>>n;
    vector<vector<int>> a(n+1,vector<int>(n+1));
    for(int i=1;i<=2*n;i++) p[i] = i;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>a[i][j];
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(i!=j)
            {
                int t1 = a[i][j],t2 = a[j][i];
                if(t1==t2) continue;
                if(t1<t2)
                {
                    if(find(i)!=find(j+n))
                    {
                        merge(i,j);
                        merge(i+n,j+n);
                    }
                }
                if(t2<t1)
                {
                    if(find(i)!=find(j))
                    {
                        merge(i,j+n);
                        merge(i+n,j);
                    }
                }
            }
    for(int i=1;i<=n;i++)
        if(find(i)!=find(1)&&find(i+n)!=find(1))
        {
            merge(1,i);
            merge(1+n,i+n);
        }
    for(int i=1;i<=n;i++)
        if(find(i)==find(1))
            for(int j=1;j<=n;j++)
                swap(a[i][j],a[j][i]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            cout<<a[i][j]<<" ";
        cout<<"\n";
    }
}
 
int main() 
{
    ios;
    int T=1;
    cin>>T;
 
    while(T -- ) {
        solve();
    }
 
    return 0;
}

标签:Div,int,t2,交换,Codeforces,t1,812,我们,define
来源: https://www.cnblogs.com/aitejiu/p/16558811.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有