• Atcoder ZEP F题


    题意

    (;)
    给定(n,m),其中(n=2^k),再给一个长度为(m)的数组(A)
    (i,j)间可以连边当且仅当(i;xor;j otin A)
    问是否有一种方案,使得这(n)个点构成一棵树,构造出这种方案
    否则输出-1
    (kleq 18)
    (;)

    Solution

    (;)
    先考虑是否有解。
    我们取出(A)的补集(B)
    你会发现,往外连边实际上就是不断异或上(B)中的元素。
    如果有解,当且仅当(b_1,b_2,cdots,b_{n-m})中某些数的异或和能够表示出(0)(n-1)的所有数
    所以,我们可以构造出(B)的线性基,判断线性基大小是否为(k)即可。
    (;)
    怎么构造方案呢?
    因为线性基中的每个元素实际上都可以表示为:成功插入线性基的一部分(b_i)的异或和
    所以我们只需要管那些成功插入到线性基中的(b_i)就可以了,而这样的数最多有(log; n)
    (;)
    然后我们就从0到(n-1)进行枚举,只使用这(k)(b_i),能往外连边就贪心地连,再用并查集维护一下连通性即可。
    (;)

    Code

    #include <bits/stdc++.h>
     
    const int N = 500010;
     
    int n, m, k, d[20], st[N], cnt, tmp[20], fa[N];
     
    void ins(int x) {
    	int X = x;
    	for (int i = k; ~i; i --) {
    		if (!(x >> i & 1)) continue;
    		if (d[i]) x ^= d[i];
    		else {
    			d[i] = x; tmp[i] = X;
    			cnt ++;
    			return;
    		}
    	}
    }
     
    int find(int x) {
    	if (fa[x] != x) fa[x] = find(fa[x]);
    	return fa[x];
    }
    int main() {
    	scanf("%d%d", &n, &m);
    	k = (int) log2(n);
    	for (int i = 1; i <= m; i ++) {
    		int x; 
    		scanf("%d", &x); st[x] = 1;
    	}
    	for (int i = 0; i < n; i ++) {
    		if (!st[i]) ins(i);
    	}
    	if (cnt < k) {
    		puts("-1");
    		return 0;
    	}
    	for (int i = 0; i < n; i ++) fa[i] = i;
    	for (int i = 0; i < n; i ++) {
    		for (int j = 0; j <= k; j ++) {
    			if (find(i) != find(i ^ tmp[j])) {
    				fa[find(i)] = find(i ^ tmp[j]);
    				printf("%d %d
    ", i, i ^ tmp[j]);
    			}
    		}
    	}
    	return 0;
    } 
    
  • 相关阅读:
    FillForm网页表单填写插件(用于火狐浏览器,自动填表,自动表单,填写表单)
    Firefox下完整读写本地文件的方法
    failure notice
    百度奖品兑换监视程序
    javascript数组及其操作(zz)
    JMETER使用
    ubuntu安装mysql
    windows live writer写csdn博客,very good~
    用SSH在linux下安装JDK和Tomcat
    java模拟HTTP请求(集合了网上搜来的各种)
  • 原文地址:https://www.cnblogs.com/czyty114/p/14725759.html
Copyright © 2020-2023  润新知