• agc016D


    题意

    题目链接

    给出两个长度为(n)的数组(a, b)

    每次可以将(a)中的某个数替换为所有数(xor)之和。

    (a)数组可以转换为(b)数组,输出最少操作次数

    否则输出(-1)

    Sol

    一般那看到这种(N leqslant 10^5)而且不可做的题肯定是先找结论啦

    不难看出,我们把所有数(xor)起来的数替换掉之后再次(xor),得到的一定是被替换掉的数。

    实际上,我们可以把xor出来的数放到一个新的位置(N+1),这样每次操作就变成了交换第(N+1)个位置的数和任意一个位置(x)的数

    总的问题就变成了

    给出两个长度为(N+1)的数组(a, b),每次可以在(a)中交换(forall i in [1, n])位置和(N+1)位置的数,问最少交换几次变为(b)数组

    首先把(-1)的情况判掉,很显然,把两个数组排序后,若存在一个位置不相同,则一定无解

    否则一定有解。

    到这里我就不会了。。。。

    官方题解非常神仙。

    对于(i)位置,若(a_i ot = b_i),则向(a_i)(b_i)连一条边

    最终答案 = 总边数 + 联通块数 - 1

    想一想为什么,对于联通块内的点,假设其大小为(x),我们一定可以通过(x-1)次操作把他们对应的(a)(b)变的相同

    对于不同联通块之间,我们还需要一步操作使得第(N+1)个位置的数在两个联通块之间转化(第一个除外)

    对于第(N+1)个位置需要单独考虑:如果它已经在联通块里则不需要考虑,否则把它看做单独联通块

    否则

    2
    1 3
    3 1
    

    可以用并查集维护联通块个数

    #include<bits/stdc++.h>
    const int MAXN = 4e5 + 10;
    using namespace std;
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    int N;
    int a[MAXN], b[MAXN], ta[MAXN], tb[MAXN], sa, sb, tot = 0, date[MAXN], fa[MAXN];
    map<int, bool> ti;
    int find(int x) {
    	return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
    }
    int unionn(int x, int y) {
    	fa[x] = y;
    }
    int main() {
    	N = read();
    	for(int i = 1; i <= N; i++) a[i] = read(), sa ^= a[i]; a[N + 1] = sa;
    	for(int i = 1; i <= N; i++) b[i] = read(), sb ^= b[i]; b[N + 1] = sb;
    	N++;
    	memcpy(ta, a, sizeof(a)); memcpy(tb, b, sizeof(b));
    	sort(ta + 1, ta + N + 1); sort(tb + 1, tb + N + 1);
    	for(int i = 1; i <= N - 1; i++) if(ta[i] != tb[i]) return puts("-1"), 0;
     
    	int ans = 0, num = 0;
    	for(int i = 1; i <= N; i++) 
    		if(a[i] != b[i] || (i == N)) {
    			date[++num] = a[i]; date[++num] = b[i];
    			if(i < N)ans++;//最后一块单独考虑
    		}
    	if(ans == 0) return puts("0"), 0;
     
    	sort(date + 1, date + num + 1);
    	num = unique(date + 1, date + num + 1) - date - 1;
    	for(int i = 1; i <= num; i++) fa[i] = i;
    	for(int i = 1; i <= N; i++)
    		if(a[i] != b[i]) {
    			a[i] = lower_bound(date + 1, date + num + 1, a[i]) - date,
    			b[i] = lower_bound(date + 1, date + num + 1, b[i]) - date;
    			if(!ti[a[i]]) ti[a[i]] = 1;
    			if(!ti[b[i]]) ti[b[i]] = 1;
    			unionn(find(a[i]), find(b[i]));
    		}
    		
    	for(int i = 1; i <= num; i++)
    		if(fa[i] == i) ans++;
    	printf("%d", ans - 1);
     
    	return 0;
    }
    
  • 相关阅读:
    iOS 多态的简单思想
    WPF SDK研究 之 AppModel
    SQL实现统计字符串出现的频次
    Python学习心得(一) 列表List
    GreenPlum之数组合并取交集及行变列、列变行函数
    SQL Server日常总结生僻小技巧 10个可能有八个平时都没用到过
    GreenPlum之按月份或季度实现行转列
    GreenPlum之生成月份字典表
    postgresql数据库中判断是否是数字和日期时间格式函数
    maven构建报错org.apache.maven.lifecycle.LifecycleExecutionException
  • 原文地址:https://www.cnblogs.com/zwfymqz/p/9715452.html
Copyright © 2020-2023  润新知