【做题】agc016d - XOR Replace——序列置换&环

原文链接 https://www.cnblogs.com/cly-none/p/9813163.html

题意:给出初始序列\(a\)和目标序列\(b\),都有\(n\)个元素。每次操作可以把\(a\)中的一个元素替换为\(a\)中所有元素的异或和,问最少操作多少次,把序列\(a\)变成序列\(b\),或判断无解。

\(n \leq 10^5\)

首先,这个“替换为所有元素的异或和”,其实就是一开始有\(v\)\(a\)中所有元素异或和,每次操作把\(a\)中某个元素与\(v\)交换。再转化一下,也就是一开始\(a_0\)\(a\)中所有元素的异或和,每次可以把\(a\)中的某个元素和\(a_0\)交换。

这样很容易能判断无解的情况。现在只考虑如何求出操作的最小次数。

先不考虑元素相等的情况。那么就变成了一个排列的置换。考虑其中的每一个环,设环大小为\(m \, (m > 1)\),假如其中不包含\(a_0\),那么这个环就需要\(m+1\)次操作来处理;否则只需要\(m-1\)次。

而当有元素相等时,我们也可以类似于排列处理。定义每一对\((a_i,b_i)\)产生一条\((a_i,b_i)\)的有向边。因为包含\(a_0\)的环的个数是固定的,所以要最小化答案,我们就要最小化环的个数。但是这道题的图存在特殊性质:每一个弱连通分量都是由若干个有向环组成的,这意味着一定存在欧拉闭迹。因此,每个连通分量都能用只一个环解决。这显然也是答案的下界。

然后还有\(a_0\)的一点细节。

  • \(a_0 \neq b_0\)。那么,\(a_0\)一定在一个大于1的环上。最终答案减二。
  • \(a_0 = b_0\),但\(a_0\)所在的弱连通分量大于1。这样,那个弱连通分量的操作个数能少一个。
  • \(a_0 = b_0\),且\(a_0\)所在弱连通分量大小为1。那它对答案没有什么影响。

时间复杂度\(O(n \log n)\)

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N],n,b[N],tmp,ans,cur,uni[N],sz[N],vis[N];
map<int,int> mp;
typedef pair<int,int> pii;
vector<int> vec;
template <typename tp>
void outputarr(tp *a,tp *b) {
  while (a != b)
    cerr << *a << ' ', ++ a;
  cerr << endl;
}
int getfa(int pos) {
  return pos == uni[pos] ? pos : uni[pos] = getfa(uni[pos]);
}
int main() {
  scanf("%d",&n);
  for (int i = 1 ; i <= n ; ++ i)
    scanf("%d",&a[i]), tmp ^= a[i];
  a[0] = tmp;
  for (int i = 1 ; i <= n ; ++ i)
    scanf("%d",&b[i]);
  for (int i = 0 ; i <= n ; ++ i)
    ++ mp[a[i]];
  for (int i = 1 ; i <= n ; ++ i) {
    if (!mp.count(b[i]))
      return puts("-1"), 0;
    if (mp[b[i]] == 0) return puts("-1"), 0;
    mp[b[i]]--;
  }
  for (int i = 0 ; i <= n ; ++ i)
    if (mp[a[i]]) b[0] = a[i];
  for (int i = 0 ; i <= n ; ++ i)
    vec.push_back(a[i]);
  sort(vec.begin(),vec.end());
  vec.erase(unique(vec.begin(),vec.end()),vec.end());
  for (int i = 0 ; i <= n ; ++ i)
    a[i] = lower_bound(vec.begin(),vec.end(),a[i]) - vec.begin() + 1, \
      b[i] = lower_bound(vec.begin(),vec.end(),b[i]) - vec.begin() + 1;
  for (int i = 0 ; i <= n ; ++ i)
    uni[a[i]] = a[i];
  for (int i = 0 ; i <= n ; ++ i) {
    int x = getfa(a[i]), y = getfa(b[i]);
    if (x != y)
      uni[x] = y;
  }
  for (int i = 0 ; i <= n ; ++ i) {
    if (a[i] == b[i]) continue;
    ++ ans;
    ++ sz[getfa(a[i])];
  }
  for (int i = 0 ; i <= n ; ++ i) {
    if (a[i] == b[i]) continue;
    if (a[i] == getfa(a[i]) && !vis[a[i]]) {
      ++ ans;
      vis[a[i]] = 1;
    }
  }
  if (a[0] != b[0]) ans --;
  if (sz[getfa(a[0])] > 1) ans --;
  printf("%d\n",ans);
  return 0;
}


小结:早上的训练赛搬了这道题。从我的FST中,也可以看出我对问题的考虑不够仔细。补题过程中思维混乱的问题也很明显,导致花费了很多时间。

posted @ 2018-10-18 21:04 莫名其妙的aaa 阅读(...) 评论(...) 编辑 收藏