T1:石子游戏
给出
n
n
n个数
a
i
a_i
ai,求删去最少的数,使得剩下的数的异或和为0。
记
A
=
m
a
x
(
a
i
)
A=max(a_i)
A=max(ai),
n
,
A
≤
500000
n,A\le500000
n,A≤500000
题目分析:
求异或和为0的最大集合不好直接求,转化为补集问题:求异或和为 ⊕ a i \oplus a_i ⊕ai的最小集合。
设 f [ i ] [ x ] f[i][x] f[i][x]表示选 i i i个数,能否取得异或和为 x x x,根据异或(线性基)的性质,可以发现最多只需要选 log A \log A logA个数就可以取得所有可能的异或和。
选数的过程可以FWT优化,复杂度 O ( n log 2 A ) O(n\log^2A) O(nlog2A),利用单点IFWT( I F W T ( a ) i = 1 n ∑ j ( − 1 ) d ( i & j ) a j IFWT(a)_i=\frac 1{n}\sum_{j}(-1)^{d(i\&j)}a_j IFWT(a)i=n1∑j(−1)d(i&j)aj)可以每次 O ( A ) O(A) O(A)check,优化到 O ( n log A ) O(n\log A) O(nlogA)。
Code:
#include<bits/stdc++.h>
#define maxn 550005
using namespace std;
const int mod = 1e9+7;
int n,a[maxn],b[maxn],A,sum,bit[maxn];
int upd(int x){return x>=mod?x-mod:x;}
void FWT(int *a,int len){
for(int i=2,l=1;i<=len;l=i,i<<=1)
for(int j=0;j<len;j+=i)
for(int k=j;k<j+l;k++){
int u=a[k],v=a[k+l];
a[k]=upd(u+v),a[k+l]=upd(u-v+mod);
}
}
bool check(){
int ret=0;
for(int i=0;i<A;i++) ret=(ret+(bit[sum&i]&1?-1:1)*b[i])%mod;
return ret!=0;
}
int main()
{
scanf("%d",&n);
for(int i=1,x;i<=n;i++) scanf("%d",&x),A=max(A,x),sum^=x,a[x]=1;
A=A?1<<int(log2(A)+1):1;
for(int i=1;i<A;i++) bit[i]=bit[i>>1]+(i&1);
FWT(a,A),b[0]=1,FWT(b,A);
for(int t=0;t<=20;t++){
if(check()) return printf("%d\n",n-t),0;
for(int i=0;i<A;i++) b[i]=1ll*b[i]*a[i]%mod;
}
}
T2:函数
n
≤
1
0
13
,
k
≤
20
n\le10^{13},k\le20
n≤1013,k≤20
题目分析:
直接Min_25筛跑
1
0
11
10^{11}
1011就已经很勉强了。
需要利用一个奇技淫巧:Powerful Number
两篇学习地址:zzq’s blog,zjp-shadow
构造一个积性函数 G ( x ) G(x) G(x)使得 G ( p ) = F ( p ) G(p)=F(p) G(p)=F(p),并且 G ( x ) G(x) G(x)的前缀和可以快速求得。这道题中 G ( x ) = x k G(x)=x^k G(x)=xk
然后求出
H
=
F
G
H={\frac FG}
H=GF,除法为狄利克雷除法,即有
F
(
p
q
)
=
∑
i
=
0
q
G
(
p
i
)
H
(
p
q
−
i
)
F(p^q)=\sum_{i=0}^qG(p^i)H(p^{q-i})
F(pq)=∑i=0qG(pi)H(pq−i),
H
H
H也是
一个积性函数。
因为 F ( p ) = G ( p ) H ( 1 ) + G ( 1 ) H ( p ) F(p)=G(p)H(1)+G(1)H(p) F(p)=G(p)H(1)+G(1)H(p),而 H ( 1 ) = G ( 1 ) = 1 , G ( p ) = F ( p ) H(1)=G(1)=1,G(p)=F(p) H(1)=G(1)=1,G(p)=F(p),所以必然有 H ( p ) = 0 H(p)=0 H(p)=0,所以只有当 x x x的所有质因子次数都大于1时 H ( x ) H(x) H(x)才不为0(H(1)=1是特例).,这样的数就是Powerful Number,它一定可以表示为 a 2 b 3 a^2b^3 a2b3的形式,易得 n n n以内的powerful number个数 O ( ∑ i = 1 n ( n i 2 ) 1 3 ) = O ( n ) O(\sum_{i=1}^n(\frac n{i^2})^{\frac 13})=O(\sqrt n) O(∑i=1n(i2n)31)=O(n)的。
这有什么用呢?考虑原来的前缀和: ∑ i = 1 n F ( i ) = ∑ i j ≤ n H ( i ) G ( j ) = ∑ i = 1 n H ( i ) ∑ j = 1 n i G ( j ) \sum_{i=1}^nF(i)=\sum_{ij\le n}H(i)G(j)=\sum_{i=1}^nH(i)\sum_{j=1}^{\frac ni}G(j) i=1∑nF(i)=ij≤n∑H(i)G(j)=i=1∑nH(i)j=1∑inG(j)
这样我们就只需要算 i i i为powerful number时的值,于是复杂度就是 O ( n ∗ c a l c ( G ) ) O(\sqrt n*calc(G)) O(n∗calc(G))
怎么求 H H H?因为是积性函数,所以只需要求 H ( p i ) H(p^i) H(pi)
由
F
(
p
q
)
=
∑
i
=
0
q
G
(
p
i
)
H
(
p
q
−
i
)
F(p^q)=\sum_{i=0}^qG(p^i)H(p^{q-i})
F(pq)=∑i=0qG(pi)H(pq−i),构造关于指数的生成函数,
G
(
x
)
G(x)
G(x)对应的生成函数为
B
(
x
)
=
∑
i
=
0
∞
p
i
k
x
i
B(x)=\sum\limits_{i=0}^\infty p^{ik}x^i
B(x)=i=0∑∞pikxi,
F
(
x
)
F(x)
F(x)对应的生成函数为
A
(
x
)
=
1
+
∑
i
=
1
∞
p
k
x
i
A(x)=1+\sum\limits_{i=1}^\infty p^kx^i
A(x)=1+i=1∑∞pkxi,那么
H
(
x
)
H(x)
H(x)对应的生成函数就应当是
A
(
x
)
B
(
x
)
\frac {A(x)}{B(x)}
B(x)A(x),即
(
1
+
∑
i
=
0
∞
p
k
x
i
)
(
1
−
p
k
x
)
(1+\sum\limits_{i=0}^\infty p^kx^i)(1-p^{k}x)
(1+i=0∑∞pkxi)(1−pkx),观察可得第
t
t
t 项的系数为
p
k
−
p
2
k
p^k-p^{2k}
pk−p2k,于是
H
(
p
t
)
=
p
k
−
p
2
k
H(p^t)=p^k-p^{2k}
H(pt)=pk−p2k。
更一般的,可以多项式求逆
O
(
t
log
t
)
O(t\log t)
O(tlogt)算出
H
(
p
t
)
H(p^t)
H(pt),当然也可以直接
O
(
t
2
)
O(t^2)
O(t2)递推(开个数组把算过的存下来,见LOJ6053)。
Code:
#include<bits/stdc++.h>
#define maxn 7000005
#define LL long long
using namespace std;
const int mod = 1e9+7;
LL n,a[maxn];
int sn,m,K,p[maxn/5],cnt,S[22][22],inv[22],pw[maxn],sum[maxn];
bool vis[maxn];
int calc(LL n,int k){
int ret=0,fac=1,N=n%mod;
for(int i=0;i<=k;i++){
fac=1ll*fac*(N+1-i)%mod;
ret=(ret+1ll*fac*S[k][i]%mod*inv[i+1])%mod;
}
return ret;
}
int ID(LL x){return x<=sn?x:m-n/x+1;}
void Powerful_Number(LL x,int k,int val){
(sum[ID(n/x)]+=val)%=mod;
for(int i=k;i<=cnt&&x<=n/p[i]/p[i];i++){
LL pp=1ll*x*p[i];
for(;pp<=n/p[i];pp*=p[i]) Powerful_Number(pp*p[i],i+1,1ll*(pw[i]-1ll*pw[i]*pw[i])%mod*val%mod);
}
}
int main()
{
scanf("%lld%d",&n,&K),sn=sqrt(n);
for(int i=S[0][0]=1;i<=K;i++) for(int j=1;j<=i;j++) S[i][j]=(S[i-1][j-1]+1ll*j*S[i-1][j])%mod;
inv[0]=inv[1]=1; for(int i=2;i<=K+1;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(LL i=1;i<=n;i++) a[++m]=i=n/(n/i);
for(int i=2;i<=sn;i++){
if(!vis[i]) {p[++cnt]=i; for(int j=pw[cnt]=1;j<=K;j++) pw[cnt]=1ll*pw[cnt]*i%mod;}
for(int j=1;j<=cnt&&(i*p[j]<=sn);j++) {vis[i*p[j]]=1;if(i%p[j]==0) break;}
}
Powerful_Number(1,1,1);
int ans=0;
for(int i=1;i<=m;i++) if(sum[i]) ans=(ans+1ll*sum[i]*calc(a[i],K))%mod;
printf("%d\n",(ans+mod)%mod);
}
T3:画(画家小P)
题目分析:
DP总状态数是
O
(
3
n
)
O(3^n)
O(3n),转移时由于每次划分出的都是
l
i
m
i
t
limit
limit最小的点,所以
T
T
T必然只能出现
i
i
i的左边,于是转移的复杂度是
O
(
∑
i
=
1
n
2
i
−
1
∗
3
n
−
i
)
=
O
(
3
n
)
O(\sum_{i=1}^n2^{i-1}*3^{n-i})=O(3^n)
O(∑i=1n2i−1∗3n−i)=O(3n)
对于
2
n
2^n
2n个子集算
F
F
F的复杂度论文里说的是
O
(
n
2
l
o
g
C
)
O(n^2logC)
O(n2logC),但实际上可以做到
O
(
n
l
o
g
C
∗
8
)
O(nlogC*8)
O(nlogC∗8):
(zhengrui【20寒假Day 2】我覀,这个题是带修改的,每一位线段树维护,可以去掉第三维用多项式(两项)维护)
但是转移的时候要转进制找到0的集合,所以最后的复杂度是 O ( 3 n n + 2 n n log C ) O(3^nn+2^nn\log C) O(3nn+2nnlogC)
Code:
#include<bits/stdc++.h>
#define maxn 16
#define LL long long
using namespace std;
const int mod = 998244353;
int n,m,id[maxn],rk[maxn],f[43046725],coef[1<<maxn],bit[1<<maxn],lg[1<<maxn],F[1<<maxn],inv2[65]={1,(mod+1)/2},pw[maxn],st[1<<maxn];
bool e[1<<maxn];
LL C,a[maxn];
bool cmp(int i,int j){return a[i]<a[j];}
int solve(int s){
static LL b[maxn]; static int f[maxn][2][2]; int N=0; LL sum=0;
for(int i=0;i<n;i++) if(s>>i&1) b[N++]=a[i],sum^=a[i];
int ret=sum==C;
for(int k=59;k>=0;k--){
int x=0;
for(int i=0;i<N;i++) x^=b[i]>>(k+1)&1;
if(x^(C>>(k+1)&1)) break;
memset(f,0,sizeof f);
f[0][0][0]=1;
for(int i=0;i<N;i++){
x=b[i]>>k&1;
for(int j=0;j<2;j++) for(int v=0;v<2;v++) for(int t=0;t<=x;t++)
f[i+1][j^t][v|(t<x)]=(f[i+1][j^t][v|(t<x)]+1ll*(t==x?(b[i]&((1ll<<k)-1))+1:1ll<<k)%mod*f[i][j][v])%mod;
}
ret=(ret+1ll*f[N][C>>k&1][1]*inv2[k])%mod;
}
return ret;
}
int main()
{
scanf("%d%d%lld",&n,&m,&C);
for(int i=0;i<n;i++) scanf("%lld",&a[i]),id[i]=i,lg[1<<i]=i;
sort(id,id+n,cmp),sort(a,a+n);
for(int i=0;i<n;i++) rk[id[i]]=i;
for(int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),x=rk[x-1],y=rk[y-1],e[1<<x|1<<y]=1;
for(int i=0;i<n;i++) for(int s=0;s<1<<n;s++) if(s>>i&1) e[s]|=e[s^1<<i]; // if T \subset S is 1, S is 1. multidegree prefix.
for(int s=1;s<1<<n;s++){
coef[s]=!e[s];
for(int t=(s-1)&s;t;t=(t-1)&s) if(t&(s&-s))
coef[s]=(coef[s]-coef[t]*(!e[s^t]))%mod;
}
for(int s=1;s<1<<n;s++) if(!((bit[s]=bit[s>>1]+(s&1))&1)) coef[s]=1ll*coef[s]*(a[lg[s&-s]]%mod+1)%mod;
for(int i=2;i<=60;i++) inv2[i]=1ll*inv2[i-1]*inv2[1]%mod;
if(!C) F[0]=1;
for(int s=1;s<1<<n;s++) F[s]=solve(s);
int N=pw[0]=1;
for(int i=1;i<=n;i++) pw[i]=N=N*3;
for(int s=1;s<1<<n;s++) st[s]=st[s>>1]*3+(s&1);
for(int s=1;s<1<<n;s++) if(bit[s]&1) st[s]+=pw[lg[s&-s]];
f[0]=1; int ans=0;
for(int s=0;s<N;s++) if(f[s]){
int t=0,fp=0;
for(int i=0;i<n;i++) if(s/pw[i]%3==0) t|=1<<i,!fp&&(fp=1<<i);
if(t){
int r=t^fp;
for(int ns=r;;ns=(ns-1)&r){
int now=s+st[fp|ns];
f[now]=(f[now]+1ll*f[s]*coef[fp|ns])%mod;
if(!ns) break;
}
}
else{
for(int i=0;i<n;i++) if(s/pw[i]%3==2) t|=1<<i;
ans=(ans+1ll*f[s]*F[t])%mod;
}
}
printf("%d\n",(ans+mod)%mod);
}