其他分享
首页 > 其他分享> > Codeforces Round #812 (Div. 2)

Codeforces Round #812 (Div. 2)

作者:互联网

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