Divan and bitwise operations CF1614C

题目的意思就是说,有一个数列一堆数,然后后他有好多子序列(不过子序列不能乱的,只能通过删除原来的元素来得到),他的和谐值是他所有子序列里的所有值进行XOR然后全部加起来得到的。(每个子序列的所有值单独进行xor,这样每个子序列都会得到一个值,全部加起来)

(到这里已经够难了吧,可是题目还是要作妖)

可是题目不给你每一个序列的值,很可惜,他给的是这个序列的许多子序列(子段)的左右位置和他们中间数的OR,x;

打个比方 ,比如(0,2)这个序列。题目会给:

2 1

1 2 2

第一个2意思是序列有两个元素,然后1代表给出1个子段

下面的1 2 2代表从第一个数到第2个数他们或OR起来值是2(0or2=2)

好家伙这慢慢分析吧。。。(看懂题目花了老久了/(ㄒoㄒ)/~~(¬︿̫̿¬☆))。

先不考率后面的东西,假如我们已经算出来这个序列了,那么怎么算出来他的所有子序列XOR的和呢。

一开始我想到是dfs就是直接用一个数组 来存储,然后一步一步向下搜索每一步都XOR一下,每一步也都加到ans上一下,当然是for循环里面的一个dfs(因为他是只能按顺序的给子序列所以想到dfs也很正常吧),可惜这题2*1e5.我想了想应该不行会超时()而且我们还不知道怎么吧子段换成原序列,好家伙白想一通。。/(ㄒoㄒ)/~~。。

想到了这是一道位运算题目,我们按位考虑一下,首先,如果有一个序列算出他的所有数的XOR,其实就是统计所有数加一起每一个位有多少个1,基数就算,偶数就不算然后转换成十进制数即可求得。

可惜这一题是求子序列,先不说我们有没有能力能求出他的原序列,即使有又怎么算那么多子序列呢。先爆力试试吧,说不定就知道了。

子序列怎么求呢,或者说有多少的子序列呢,按照排列组合的知识我们知道应该是有(C(1,n)+C(2,n)+C(3,n)++++++。。。。++C(n,n))=2的n次方(因为是按顺序的)好家伙这么多(要知道n是上万的啊),肯定会超时了。。/(ㄒoㄒ)/~~。。。然后一筹莫展。。唉。

过了一会(第二天),我们再看看位运算,发现好像可以按位来找啊,我们对原序列直接进行排列组合貌似不是太好,我们求的是XOR的和,所以如果有(0,1)排列组合只有01的话会好很多.

      我们假设是在算他这个序列的XOR和,我们把每一个位有多少1算出来,因为我们是按顺序任何数都能组合,那我们一个位上的数也可以组合一下呢,比如我们把某个位上有三个1.他是什么意思呢,代表n个数里只有三个数上这个位有1,好家伙思路直接出来了,我们只要取到3个数中的一个数不就行了C(1,3)然后他们可以和这个位上是0的数进行组合(0的数可以想取多少就多少)且0的数有n-3个,即C(1,3)*pow(2,n-3).。。这么想,貌似还可以取3个1也可以啊,XOR后也不是0的。就是

C(3,3)*pow(2,n-3).行了,那么就有了,假设每一位有t个1.

那么就是(C(1,t)+C(3,t)+...+C(t,t))*pow(2,n-t)t为奇数。

(C(1,t)+C(3,t)+...+C(t-1,t))*pow(2,n-t)t为偶数。那么这些C都可以预处理出来,就不会超时了( •̀ ω •́ )y

那么这是这个位存在的次数。并且每存在一次应该是算pow(2,i-1)大小的。即前面的式子×pow(2,i-1)i表示第几位二进制。就是说把每一位的这个算出来加一下就行了。

行了来了

那么怎么算出来原序列呢,不知道。。一筹莫展/(ㄒoㄒ)/~~

但是我们又来想想,这是排列组合谔。

运用数学排列组合知识我们发现其实就是pow(2,n-1)(括号里的一堆C其实就是pow(2,k-1),高中排列组合公式。),好家伙,居然与k没有关系,只是有没有1就行了,我擦,那我们OR运算不就是保存1的吗。好家伙我们直接再来一层OR不就行了,把1全部放进来。。然后又有,我们每一位都来一次,都与k无关,又是每一位,那甚至不用单独算每一位,直接×一下pow(2,n-1)不就行了。。好家伙直接上代码!。

#include<iostream>
#include<algorithm>
#define N 200010
#define idx 1000000007
long long int er[N];//预处理数组 
int main(void)
{
	int t;
	er[0] = 1;
	scanf("%d",&t);
	for(int i = 1;200000 >= i;i++)//预处理 
	{
		er[i]=(er[i-1]*2)%idx;
	}
	for(int i = 1;t >= i; i++)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		long long int ans=0;
		while(m--)
		{
			long long int l,r,x;
			scanf("%lld%lld%lld",&l,&r,&x);//输入 
			ans|=x;//保存每一位的1.
		}
		printf("%lld\n",(ans*er[n-1])%idx);//输出咯 
	}
	return 0;
}

by————若根。(*^_^*)(*^_^*)。

  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值