zoukankan      html  css  js  c++  java
  • 7379. 【2021.11.12NOIP提高组联考】数数

    Description

    ( ext{xyx}) 是一个喜欢数数的触手怪。

    他很喜欢异或!于是他摸出一个数列 (left{a_{n} ight})。 并在上面做了一番操作, 每次操作形如 (a_{i}:=a_{i} oplus a_{i-1}(1<i leq n)), 其中 (oplus) 表示异或。

    他想知道, 在任意次操作后, 他能从这个初始数列得到多少不同的数列。

    Solution

    比较结论的一道题目。

    首先讲一个叫做线性基的东西,具体定义如下:

    由一个集合构造出来的另一个集合,它有以下几个性质:

    • 线性基的元素能相互异或得到原集合的元素的所有相互异或得到的值。
    • 线性基是满足性质 1 的最小的集合。
    • 线性基没有异或和为 0 的子集。
    • 线性基中每个元素的异或方案唯一,也就是说,线性基中不同的异或组合异或出的数都是不一样的。
    • 线性基中每个元素的二进制最高位互不相同。

    那么线性基对这道题目有什么用呢?

    想一下原序列的两个数异或,相当于在线性基内若干个异或起来的个数,也就是说,第 (i) 位置的答案就是在这之前,2 的线性基内元素个数次方,即线性基内元素个数为 (x),那么答案就是 (2^x)

    统计完 (i) 的答案后要将 (a_i) 插入线性基,插入线性基的方法这里不在赘述,读者可以自行搜索,这里给出插入的代码:

    void insert(int x)
    {
    	for (int i=30;i>=0;--i)
    	{
    		if (!(x&(1<<i))) continue;
    		if (!p[i])
    		{
    			p[i]=x;
    			++num;
    			break;
    		}
    		x^=p[i];
    	}
    }
    

    那么最后的答案就是 (Pi 2^x)

    Code

    #include<cstdio>
    #define N 100005
    #define mod 998244353
    using namespace std;
    int n,ans,num,a[N],p[31];
    void insert(int x)
    {
    	for (int i=30;i>=0;--i)
    	{
    		if (!(x&(1<<i))) continue;
    		if (!p[i])
    		{
    			p[i]=x;
    			++num;
    			break;
    		}
    		x^=p[i];
    	}
    }
    int main()
    {
    	freopen("count.in","r",stdin);
    	freopen("count.out","w",stdout);
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	ans=1;
    	for (int i=1;i<=n;++i)
    	{
    		ans=(ans*(long long)(1<<num)%mod)%mod;
    		insert(a[i]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    iOS:不同属性声明方式的解析
    iOS:图像和点击事件
    iOS:NAV+TABLE结合
    iOS:实现表格填充和选择操作
    iOS: 填充数据表格
    iOS:导航栏的工具条和导航条
    iOS:使用导航栏
    hello,world不使用ARC
    iOS代码实现:创建按钮,绑定按钮事件,读取控件值
    iOS版 hello,world版本2
  • 原文地址:https://www.cnblogs.com/Livingston/p/15546278.html
Copyright © 2011-2022 走看看