洛谷 P4869 albus就是要第一个出场(线性基,异或值排名)

传送门


解题思路

首先背过线性基的一个性质吧(因为我不会证明):
线性基里的每一个异或值出现的次数相等,为 \(2^{n-k}\)
然后这题就变成统计某个数是第几大异或值了。
分类讨论在归纳一下:
假设询问的数字是q。当处理到第i位且q的这一位为1,且线性基这一位有数,则

  • 当前面位异或起来得到的这一位已经是1,则可以通过让其他数异或上a[i]来减小其他数,使其小于q。
  • 当前面位异或起来得到的这一位是0,则q必须要异或上a[i],但是别的数可以通过不异或a[i]使其小于q。

综上,第i位对答案是否产生贡献只与q的第i位原来是否为1有关,与现在的状态无关。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<vector>
using namespace std;
const int mod=10086;
int n,x,a[35],cnt,q,ans[35];
int ksm(int a,int b){
	if(b==0) return 1;
	if(b==1) return a;
	int res=ksm(a,b/2);
	if(b&1) return res*res%mod*a%mod;
	return res*res%mod;
}
void insert(int x){
	for(int i=30;i>=0;i--){
		if(x&(1<<i)){
			if(a[i]) x^=a[i];
			else{
				a[i]=x;
				return;
			}
		}
	}
}
int query(int x){
	int res=0;
	for(int i=30;i>=0;i--){
		if((x&(1<<i))&&(a[i])){
			res=(res+ksm(2,ans[i]))%mod;
		}
	}
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>x,insert(x);
	for(int i=0;i<=30;i++) if(a[i]) ans[i]=cnt++;
	cin>>q;
	cout<<(ksm(2,n-cnt)*query(q)+1)%mod;
	return 0;
}
posted @ 2021-10-28 17:53  尹昱钦  阅读(92)  评论(0编辑  收藏  举报