校内NOI模拟测试007T1 送分(score) “简单”的计数

问题描述

校内评测链接

良心出题人准备为大家送出大量的分数。

出题人生成了一个序列,其中第\(𝑖\)个元素为\(a_i\)

定义一个集合\(𝑆\)的分值为

\[F(S,x) = \sum_{T \subseteq S} G(T)·x^{|T|} \]

其中𝑥为给 定参数,𝐺(𝑇)表示𝑇中所有元素的异或和,空集的异或和为0。 出题人每次会选择一个区间\([𝑙, 𝑟]\)和一个参数\(𝑥\),令第\(𝑙\)到第\(𝑟\)个元素组成的集合为\(𝑆\),出题人会送给一位同学\(𝐹(𝑆, 𝑥)\)的分数。 你想知道每个同学获得了多少分数,因为分数实在太多了, 你只想要知道分数对\(998244353\)取模后的结果。

题意简述

给一个序列a,设一个区间S分值为

\[F(S,x) = \sum_{T \subseteq S} G(T)·x^{|T|} \]

有m个询问,每次给\(l\),\(r\),\(x\),求\(F([l,r],x)\)

即,求给定区间内的所有集合的异或和*\(x^{集合大小}\)

集合为可重集合。

分析

因为式子中有一个集合大小的贡献,所以无法预处理,只能直接搞。

考虑拆位,设这个区间长度为\(n\),区间内的第k位有\(m\)\(1\)

\(1\)的个数为偶数,则没有贡献。而\(0\)的贡献很好求,虽然不会对答案有直接影响,但是充当了集合的大小的一部分,所以它的贡献是一个系数。

可以计算出贡献:

\[\sum_{i=0}^{n-m}\sum_{j \nmid 2}^m{C_{n-m}^i \cdot x^i \times C_m^j \cdot x^j} \]

将无关项分离:

\[\sum_{i=0}^{n-m}{C_{n-m}^i \cdot x^i }\times { \sum_{j \nmid 2}^m C_m^j \cdot x^j} \]

观察到形如二项式定理

\[\sum_{i=0}^n {C_n^i \cdot x^i} = (x+1)^n \]

的两个式子,左边的可以用二项式定理直接变成\((x+1)^n\),即

\[(x+1)^n = { \sum_{j \nmid 2}^m C_m^j \cdot x^j} \]

那么右边的这个\(j \texttt{ mod } 2 ==1\)怎么转化呢?类似共轭,

\[(1+(-x))^n=\sum_{i=0}^n {C_n^i \cdot (-x)^i} \]

两式相减

\[(x+1)^n-(-x+1)^n=2 \sum_{i \nmid 2} {C_n^i \cdot x^i} \]

成功了!再除以\(2\)就是右半部分了。

所以答案就是

\[(x+1)^{n-m} \cdot \frac{(x+1)^m-(1-x)^m}{2} \]

用前缀和计算区间内第\(k\)位上\(1\)的个数\(m\),再加上快速幂就好了,记得\(\texttt{long long}\)取模,除以\(2\)的话记得用逆元

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define p 998244353
#define IL inline
#define re register
#define LL long long
#define ULL unsigned int
#define re register
#define debug printf("Now is %d\n",__LINE__);
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
}
inline int read()
{
	int x=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int a[100010];
LL sum[100010][32];
int n;
int m;
LL qpow(LL a,LL b)
{
	int ans=1;
	for(;b;b>>=1,a=a*a%p)
		if(b&1) ans=ans*a%p;
	return ans;
}
int main()
{
	n=read();
	m=read();
	for(LL i=1;i<=n;i++)
	{
		a[i]=read();
		for(int j=30;j>=0;j--)
			sum[i][j]=sum[i-1][j]+((a[i]>>j)&1);
	}
	for(LL l,r,x,i=0;i<m;i++)
	{
		l=read();
		r=read();
		x=read();
		LL ans=0;
		for(LL j=30;j>=0;j--)
		{
			LL N=r-l+1ll,M=(sum[r][j]-sum[l-1ll][j]+p)%p;
			ans=(ans+(1ll<<j)%p*qpow(x+1ll,N-M)%p*(qpow(x+1ll,M)+p-qpow((1ll-x+p)%p,M))%p*qpow(2,p-2)%p)%p;
		}
		cout<<ans<<endl;
	}
	return 0;
}

结语

多取模啊!多取模!不然的话就会……

posted @ 2020-11-03 15:43  Vanilla_chan  阅读(153)  评论(0编辑  收藏  举报