AND Minimum Spanning Tree(HDU-6614)

Problem Description

You are given a complete graph with N vertices, numbered from 1 to N. 
The weight of the edge between vertex x and vertex y (1<=x, y<=N, x!=y) is simply the bitwise AND of x and y. Now you are to find minimum spanning tree of this graph.

Input

The first line of the input contains an integer T (1<= T <=10), the number of test cases. Then T test cases follow. Each test case consists of one line containing an integer N (2<=N<=200000).

Output

For each test case, you must output exactly 2 lines. You must print the weight of the minimum spanning tree in the 1st line. In the 2nd line, you must print N-1 space-separated integers f2, f3, … , fN, implying there is an edge between i and fi in your tree(2<=i<=N). If there are multiple solutions you must output the lexicographically smallest one. A tree T1 is lexicographically smaller than tree T2, if and only if the sequence f obtained by T1 is lexicographically smaller than the sequence obtained by T2.

Sample Input

2
3
2

Sample Output

1
1 1
0
1

题意:t 组数据,每组给出一个数 n 代表一个完全无向图的点的个数,对于任意两点 x、y,他们的权重为 x 与 y 的标号按位与,求该图的最小生成树,并输出建边的方案

思路:

由于给出的图是完全无向图,任意两点都有一条边,我们要做的就是选择边权尽量小的边,使得图能成为一个最小生成树

根据点的标号,分为奇偶两种情况:

  • 如果某点 x 是偶数:那么该点 x 的最低位一定是 0,此时只要选择与 1 号点相连,那么权值为 0,保证最小
  • 如果某点 x 是奇数:那么优先选择跟最右边的 0 的位置的 100...相连,比如 1110111就与 0001000 相连,这样使得 x&y=0,如果对应的点取不到,即该点对应标号大于 n 时,那么就选择与 1 号点相连,使得权值为 1

因此,我们只需要获得所有点的标号对应二进制位最右边 0 的位置,即可进行建边然后进行统计,利用 lowbit 即可解决这个问题

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL multMod(LL a,LL b,LL mod){ a%=mod; b%=mod; LL res=0; while(b){if(b&1)res=(res+a)%mod; a=(a<<=1)%mod; b>>=1; } return res%mod;}
LL quickPowMod(LL a, LL b,LL mod){ LL res=1,k=a; while(b){if((b&1))res=multMod(res,k,mod)%mod; k=multMod(k,k,mod)%mod; b>>=1;} return res%mod;}
LL getInv(LL a,LL mod){ return quickPowMod(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-10;
const int MOD = 998244353;
const int N = 200000+5;
const int dx[] = {-1,1,0,0,1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;

int pos[N], res[N];
int lowbit(int x) { 
    return x & (-x); 
}
int init() {//获取所有点最右边0的位置
    for (int i = 1; i <= 200000; i++) {
        if (i%2)//奇数
            pos[i] = lowbit(i + 1);
        else//偶数
            pos[i] = 1;
    }
}
int main() {
    init();
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        scanf("%d", &n);
        
        int mst=0;
        for (int i = 2; i <= n; i++) {
            if(i%2==0)//对于偶数来说,该点与1号点建边,权值为0
                res[i]=1;
            else{//对于奇数来说
                if(pos[i]>n){//当最右边0的位置大于n时,不存在对应方案,则与1号点建边,权值为1
                    mst++;
                    res[i]=1;
                }
                else{//当最右边0的位置小于等于n时,存在对应方案,与对应点建边,权值为0
                    res[i]=pos[i];
                }
            }
        }

        printf("%d\n", mst);
        for(int i=2;i<=n-1;i++)
            printf("%d ",res[i]);
        printf("%d\n",res[n]);
    }
    return 0;
}
发布了1871 篇原创文章 · 获赞 702 · 访问量 194万+

猜你喜欢

转载自blog.csdn.net/u011815404/article/details/102304687