NTT,FWT模板总结
NTT
const int N = 3 * 1e6 + 10, mod = 998244353, G = 3, Gi = 332748118;
int n, m, limit = 1, L, r[N];
ll a[N], b[N];
ll ksm(ll a, ll b) {
ll ret = 1;
while(b) {
if(b & 1) ret = (ret * a ) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ret;
}
void NTT(ll *A, int type) {
for(int i = 0; i < limit; i++)
if(i < r[i]) swap(A[i], A[r[i]]);
for(int mid = 1; mid < limit; mid <<= 1) {
ll Wn = ksm( type == 1 ? G : Gi , (mod - 1) / (mid << 1));
for(int j = 0; j < limit; j += (mid << 1)) {
ll w = 1;
for(int k = 0; k < mid; k++, w = (w * Wn) % mod) {
int x = A[j + k], y = w * A[j + k + mid] % mod;
A[j + k] = (x + y) % mod,
A[j + k + mid] = (x - y + mod) % mod;
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i = 0; i <= n; i++){
scanf("%d",&a[i]);
a[i] = (a[i] + mod) % mod;
}
for(int i = 0; i <= m; i++){
scanf("%d",&b[i]);
b[i] = (b[i] + mod) % mod;
}
while(limit <= n + m) limit <<= 1, L++;
for(int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
NTT(a, 1);NTT(b, 1);
for(int i = 0; i < limit; i++) a[i] = (a[i] * b[i]) % mod;
NTT(a, -1);
ll inv = ksm(limit, mod - 2);
for(int i = 0; i <= n + m; i++)
printf("%d ", (a[i] * inv) % mod);
return 0;
}
FWT
2020牛客暑期多校训练营(第二场)E Exclusive OR]
自卷最多19次后就不会再产生更大的值。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=998244353;
int N=1<<18;
const int inf=1e9;
ll ksm(ll a,ll b){
ll ret=1;
while(b){
if(b&1) ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
int inv2=ksm(2,mod-2);
void FWT_or(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[i+j+k]=(a[j+k]+a[i+j+k])%mod;
else a[i+j+k]=(a[i+j+k]+mod-a[j+k])%mod;
}
void FWT_and(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[j+k]=(a[j+k]+a[i+j+k])%mod;
else a[j+k]=(a[j+k]+mod-a[i+j+k])%mod;
}
void FWT_xor(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=a[j+k],Y=a[i+j+k];
a[j+k]=(X+Y)%mod;a[i+j+k]=(X+mod-Y)%mod;
if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%mod,a[i+j+k]=1ll*a[i+j+k]*inv2%mod;
}
}
int n,a[1<<19],b[1<<19],ans[200010];
int main(){
//ios::sync_with_stdio(false);
//freopen("in","r",stdin);
scanf("%d",&n);
rep(i,1,n){
int x;
scanf("%d",&x);
a[x]=b[x]=1;
}
FWT_xor(b,1);
for(int k=1;k<=19;k++){
rep(i,0,N-1) if(a[i]) ans[k]=i,a[i]=1;
FWT_xor(a,1);
rep(i,0,N-1) a[i]=1ll*a[i]*b[i]%mod;
FWT_xor(a,-1);
}
for(int k=20;k<=n;k++) ans[k]=ans[k-2];
rep(i,1,n) printf("%d%c",ans[i],i==n?'\n':' ');
return 0;
}
FWT+珂朵莉树
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=998244353;
int N=1<<18;
const int inf=1e9;
struct node
{
ll l,r; //范围
mutable ll v; //数值
node(ll L, ll R=-1, ll V=0):l(L), r(R), v(V) {}
bool operator<(const node& o) const //重载运算符
{
return l < o.l;
}
};
set<node> s;
#define IT set<node>::iterator //太长了
IT split(ll pos)
{
IT it = s.lower_bound(node(pos)); //找到首个不小于pos的set
if (it != s.end() && it->l == pos) //无需,直接返回
return it;
--it; //否则一定在前一个区间中
int L = it->l, R = it->r; //【l,r】就是要分裂的区间
ll V = it->v; //取出值
s.erase(it); //删除原集合
s.insert(node(L, pos-1, V)); //构建前半段的新结合
return s.insert(node(pos, R, V)).first; //构建后半段的新集合并且返回地址
}
void assign_val(ll l, ll r, ll val)
{
IT itr = split(r+1),itl = split(l); //求出要被摊平区间的收尾地址
s.erase(itl, itr); //删除原集合
s.insert(node(l, r, val)); //添加新集合
}
void add(ll l, ll r)
{
IT itr = split(r+1),itl = split(l);
ll cnt=0,mn=0;
for (; itl != itr; ++itl){
itl->v =(ll)sqrt(itl->v);
if(itl->v!=mn){
mn=itl->v;
cnt++;
}
}
if(cnt==1) assign_val(l,r,mn);
}
ll qy(ll x){
IT itr = split(x+1),itl = split(x);
for (; itl != itr; ++itl){
return itl->v;
}
}
void FWT_or(ll *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[i+j+k]=a[j+k]+a[i+j+k];
else a[i+j+k]=(a[i+j+k]-a[j+k]);
}
int n,m;
ll a[1<<19],b[1<<19],l[100010],r[100010],x[100010];
ll c[1000010],tot,f[1000010];
int main(){
//ios::sync_with_stdio(false);
//freopen("in","r",stdin);
scanf("%d",&n);
rep(i,1,n){
int x;
scanf("%d",&x);
a[x]++;
}
rep(i,1,n){
int x;
scanf("%d",&x);
b[x]++;
}
scanf("%d",&m);
FWT_or(a,1);FWT_or(b,1);
rep(i,0,N-1) a[i]=a[i]*b[i];
FWT_or(a,-1);
ll L=0;
rep(i,0,N-1){
if(a[i]){
s.insert(node(L+1,L+a[i],i));
}
L+=a[i];
}
rep(i,1,m){
scanf("%lld",&l[i]);
if(l[i]==0){
scanf("%lld",&x[i]);
printf("%lld\n",qy(x[i]));
}else{
scanf("%lld",&r[i]);
add(l[i],r[i]);
}
}
return 0;
}