原文链接 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))。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#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("%dn",ans); return 0; }
小结:早上的训练赛搬了这道题。从我的FST中,也可以看出我对问题的考虑不够仔细。补题过程中思维混乱的问题也很明显,导致花费了很多时间。
转载于:https://www.cnblogs.com/cly-none/p/9813163.html
最后
以上就是孤独楼房最近收集整理的关于【做题】agc016d - XOR Replace——序列置换&环的全部内容,更多相关【做题】agc016d内容请搜索靠谱客的其他文章。
发表评论 取消回复