Loading

7379. 【2021.11.12NOIP提高组联考】数数

Description

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

他很喜欢异或!于是他摸出一个数列 \(\left\{a_{n}\right\}\)。 并在上面做了一番操作, 每次操作形如 \(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\n",ans);
	return 0;
}
posted @ 2021-11-12 20:23  Thunder_S  阅读(117)  评论(0编辑  收藏  举报