Codeforces 1163E Magical Permutation [线性基,构造]

codeforces

思路

我顺着图论的标签点进去的,却没想到……

可以发现排列内每一个数都是集合里的数异或出来的。

考虑答案的上界是多少。如果能用小于\(2^k\)的数构造出\([0,2^k-1]\)内所有的数,那么答案就对这个\(k\)取\(\max\)。很显然这一定是上界。

考虑能不能构造出一组解。把\([1,2^k-1]\)的数拎出来插入线性基里得到一组极大线性无关组,那么显然它的\(size\)就是\(k\)。由于它线性无关,所以任意选取一个子集得到的异或和都不会相同,所以考虑把\(0\)放在左边,然后每次异或上线性无关组里的一个元素,取遍所有集合。

取集合可以递归进行:对于大小为\(m\)的集合,先把\(m-1\)的取遍,然后取第\(m\)个元素,然后再把\(m-1\)的集合取一遍,就可以保证相邻的集合只有一个位置不同,并且所有集合两两不同。

代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 303030
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char __sr[1<<21],__z[20];int __C=-1,__zz=0;
    inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
    inline void print(register int x)
    {
        if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
        while(__z[++__zz]=x%10+48,x/=10);
        while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
    }
    void file()
    {
        #ifdef NTFOrz
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifdef NTFOrz
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,K=20,m;
int a[sz],b[sz],lg2[sz];

int lb[sz],cnt;
bool insert(int x)
{
    drep(i,K,0) if (x>>i&1)
    {
        if (!lb[i]) return lb[i]=x,++cnt,1;
        x^=lb[i];
    }
    return 0;
}
int cur;
void dfs(int m)
{
    if (m==0) printf("%d ",cur);
    else dfs(m-1),cur^=b[m],dfs(m-1);
}

int main()
{
    file();
    read(n);
    rep(i,1,n) read(a[i]);
    sort(a+1,a+n+1);
    int ans=0;
    rep(i,2,sz-1) lg2[i]=lg2[i>>1]+1;
    for (int k=0,i=1;k<=K;k++)
    {
        while (i<=n&&lg2[a[i]]==k) insert(a[i]),++i;
        if (cnt==k+1) ans=k+1;
    }
    printf("%d\n",ans);
    rep(i,0,K) lb[i]=0;
    rep(i,1,n) if (lg2[a[i]]<ans&&insert(a[i])) b[++m]=a[i];
    dfs(m);
    return 0;
}

原文地址:https://www.cnblogs.com/p-b-p-b/p/11741238.html

时间: 2024-10-08 18:37:47

Codeforces 1163E Magical Permutation [线性基,构造]的相关文章

[TJOI2008] 彩灯 (线性基)

[TJOI2008] 彩灯 题目描述 Peter女朋友的生日快到了,他亲自设计了一组彩灯,想给女朋友一个惊喜.已知一组彩灯是由一排N个独立的灯泡构成的,并且有M个开关控制它们.从数学的角度看,这一排彩灯的任何一个彩灯只有亮与不亮两个状态,所以共有\(2^N\)个样式.由于技术上的问题,Peter设计的每个开关控制的彩灯没有什么规律,当一个开关被按下的时候,它会把所有它控制的彩灯改变状态(即亮变成不亮,不亮变成亮).假如告诉你他设计的每个开关所控制的彩灯范围,你能否帮他计算出这些彩灯有多少种样式可

魔法弹(线性基)

魔法弹(线性基) 给出n个m位数,问它们张成的异或集合的大小. 关于线性基,这篇博文讲的太好了: 所谓线性基,就是线性代数里面的概念.一组线性无关的向量便可以作为一组基底,张起一个线性的向量空间,这个基地又称之为线性基.这个线性基的基底进行线性运算,可以表示向量空间内的所有向量,也即所有向量可以拆成基底的线性组合. 在ACM领域,线性基主要用来处理有关异或和的极值问题.根据异或按照二进制数位进行的方式,我们可以把一个数字拆成它的二进制表示形式,而这个二进制形式拆成一位一位的,可以用向量来表示.显

学习:数学----线性基

线性基主要解决关于一些数的异或等问题,其中包括解决一堆数中任意几个数异或的最大值,最小值,第k大值等等. 线性基介绍及特点 前言 线性基对于萌新来说刚开始学肯定有点难度的,网上很多博客都把线性基讲复杂了(一开始就讲什么线性无关,什么张成),虽然学过线性代数再来理解线性基的确很容易,但是没学过线性代数而来学习线性基却也没有很多很难得地方(至少你知道异或吧). 所以写这篇博客直接从线性基的特点和作用来讲解,进而来讲其他操作,不会非常涉及到线性代数的某些专业知识. 线性基的组成 当我们有了一组确切的数

CF1163E Magical Permutation【线性基,构造】

题目描述:输入一个大小为\(n\)的正整数集合\(S\),求最大的\(x\),使得能构造一个\(0\)到\(2^x-1\)的排列\(p\),满足\(p_i\oplus p_{i+1}\in S\) 数据范围:\(n,S_i\le 2^{18}\) 什么?NTF在很多年前就把这东西给切了? 首先要把\(S\)缩成一个大小为\(x\)的线性无关组,而且每个数\(<2^x\),这样就可以构造出\(p\)了.(之后再说) 直接丢进线性基里就可以了吗?不行,应该是把\(<2^x\)的数全部加进去之后,看

Codeforces 724 G Xor-matic Number of the Graph 线性基+DFS

G. Xor-matic Number of the Graph http://codeforces.com/problemset/problem/724/G 题意:给你一张无向图.定义一个无序三元组(u,v,s)表示u到v的(不一定为简单路径)路径上xor值为s.求出这张无向图所有不重复三元组的s之和.1≤n≤10^5,1≤m≤2*10^5. 想法: 如果做过[Wc2011 xor]这道题目(题解),那么问题变得简单起来了. ①假设我们钦定一个(u,v),设任意一条u->v的路径xor值为X,

Codeforces 482A Diverse Permutation(构造)

题目链接:Codeforces 482A Diverse Permutation 题目大意:给定N和K,即有一个1~N的序列,现在要求找到一个排序,使得说所有的|pi?pi+1|的值有确定K种不同. 解题思路:构造,1,K+1,2,K,3,K-1,... K+2,K+3 ... N. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn

构造 CodeForces 483C Diverse Permutation

题目传送门 1 /* 2 构造:首先先选好k个不同的值,从1到k,按要求把数字放好,其余的随便放.因为是绝对差值,从n开始一下一上, 3 这样保证不会超出边界并且以防其余的数相邻绝对值差>k 4 */ 5 /************************************************ 6 Author :Running_Time 7 Created Time :2015-8-2 9:20:01 8 File Name :B.cpp 9 ********************

CodeForces 587 E.Duff as a Queen 线段树动态维护区间线性基

https://codeforces.com/contest/587/problem/E 一个序列, 1区间异或操作 2查询区间子集异或种类数 题解 解题思路大同小异,都是利用异或的性质进行转化,std和很多网友用的都是差分的思想,用两棵线段树 第一棵维护差分序列上的线性基,第二棵维护原序列的异或区间和,两者同时进行修改 考虑两个序列 $(a,b)(d,e)$,按照std的想法,应该是维护$(0 \^ a,a \^ b)(0 \^ d,d \^ e)$ 然后合并首尾变成$(0 \^ a,a \^

Codeforces 938G 线段树分治 线性基 可撤销并查集

Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问x到y的路径异或最小值 保证图在任意时刻连通 首先连通图路径异或相当于从x到y的任意一条路径再异或上若干个环得到的,只要在dfs过程中把非树边成的环丢到线性基里就好了,其他环一定可以通过这些环异或组合出来 有加边删边操作怎么做呢?线段树时间分治!注意到不能保证在线段树的任意一个节点图是连通的,需要用