luogu P3760 [TJOI2017]异或和
题面传送门
这道题首先考虑拆位,但是有加减法所以感觉很难做。
考虑另一种方法,把每种值出现的次数算出来然后再计算。
设\(ans_i\)为区间和为\(i\)出现的次数,\(f_i\)为前缀和为\(i\)的出现次数。
那么就有\(ans_i=\sum\limits{f_j\times f_{i+j}}\)
然后显然翻一下一个就可以卷积了。
时间复杂度\(O(nlogn)\)不知道为什么被树状数组两个log吊起来打。
code:
#include <vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<set>
#include<map>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db long double
#define N 3000039
#define eps (1e-14)
#define mod 998244353
#define U unsigned
using namespace std;
int n,m,k,x,y,z,tr[N];ll a[N],b[N];
I void swap(ll &x,ll &y){x^=y^=x^=y;}
I ll mpow(ll x,int y=mod-2){ll ans=1;while(y)(y&1)&&(ans=ans*x%mod),x=x*x%mod,y>>=1;return ans;}
const ll G=3,inv=mpow(G);
I void ntt(ll *f,int n,int flag){
int i,j,k;ll now,pus,key;for(i=0;i<n;i++) if(i<tr[i]) swap(f[i],f[tr[i]]);
for(i=2;i<=n;i<<=1){
for(key=mpow(flag?G:inv,(mod-1)/i),j=0;j<n;j+=i){
for(now=1,k=j;k<j+i/2;k++)pus=f[k+i/2]*now%mod,f[k+i/2]=(f[k]-pus+mod)%mod,f[k]=(f[k]+pus)%mod,now=now*key%mod;
}
}
}
int main(){
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
re int i;scanf("%d%d",&n,&m);for(i=0;i<=n;i++) scanf("%lld",&a[i]);for(i=0;i<=m;i++) scanf("%lld",&b[i]);for(m+=n,n=1;n<=m;n<<=1);
for(i=0;i<n;i++) tr[i]=(tr[i>>1]>>1)|((i&1)?(n>>1):0);ntt(a,n,1);ntt(b,n,1);for(i=0;i<n;i++)a[i]=a[i]*b[i]%mod;ntt(a,n,0);
k=mpow(n);for(i=0;i<=m;i++) printf("%lld ",a[i]*k%mod);
}