T1:染色问题
对摆在玩家面前的一排初始无色的格子进行染色,每次染色可以将连续一段格子染成一种之前没有出现过的颜色,要求在所有操作后每一个格子都被染了至少一次色(重复染色会使前一次染的颜色被覆盖)
给出长度
n
n
n和染色次数
m
m
m,求最终可能的颜色序列种数。
n
,
m
≤
1
0
6
n,m\le10^6
n,m≤106
题解:
上述的倍增FFT的具体实现方式是递归
[
l
,
r
]
[l,r]
[l,r],如果
r
−
l
+
1
r-l+1
r−l+1为偶数,则从
∏
i
=
l
m
i
d
(
x
+
i
)
→
∏
i
=
l
m
i
d
(
x
+
m
i
d
+
i
)
\prod_{i=l}^{mid}(x+i)\to\prod_{i=l}^{mid}(x+mid+i)
∏i=lmid(x+i)→∏i=lmid(x+mid+i).
设前面一个多项式的系数表达式为
∑
i
=
0
n
a
i
x
i
\sum_{i=0}^{n}a_ix^i
∑i=0naixi,那么后一个多项式就是
∑
i
=
0
n
a
i
(
x
+
m
i
d
)
i
=
∑
i
=
0
n
a
i
∑
j
=
0
i
(
i
j
)
⋅
x
j
⋅
m
i
d
i
−
j
=
∑
j
=
0
n
x
j
∑
i
=
j
n
a
i
(
i
j
)
∗
m
i
d
i
−
j
\sum_{i=0}^na_i(x+mid)^i=\sum_{i=0}^na_i\sum_{j=0}^i\binom ij\cdot x^j\cdot mid^{i-j}=\sum_{j=0}^nx^j\sum_{i=j}^na_i\binom ij*mid^{i-j}
∑i=0nai(x+mid)i=∑i=0nai∑j=0i(ji)⋅xj⋅midi−j=∑j=0nxj∑i=jnai(ji)∗midi−j
记新多项式为
B
(
x
)
B(x)
B(x),令
A
i
=
a
i
∗
i
!
A_i=a_i*i!
Ai=ai∗i!,
C
i
=
m
i
d
i
i
!
C_i={mid^i\over i!}
Ci=i!midi,那么有
B
j
∗
j
!
=
∑
i
=
j
n
A
i
∗
C
i
−
j
{B_j* j!}=\sum_{i=j}^nA_i*C_{i-j}
Bj∗j!=i=j∑nAi∗Ci−j
将
C
C
C反转,记
D
i
=
C
n
−
i
D_i=C_{n-i}
Di=Cn−i,则
B
j
∗
j
!
=
P
n
+
j
=
∑
i
=
j
n
A
i
∗
D
n
−
(
i
−
j
)
B_j*j!=P_{n+j}=\sum_{i=j}^nA_i*D_{n-(i-j)}
Bj∗j!=Pn+j=∑i=jnAi∗Dn−(i−j)
Code:
#include<bits/stdc++.h>
#define maxn 1100005
using namespace std;
const int mod = 998244353;
int n,m,fac[maxn],inv[maxn],w[maxn],wn,r[maxn];
typedef vector<int> Poly;
inline int Pow(int a,int b){
int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
return s;
}
inline int C(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
void init(){
int N=max(n,m);
fac[0]=inv[0]=1;
for(int i=1;i<=N;i++) fac[i]=1ll*fac[i-1]*i%mod; inv[N]=Pow(fac[N],mod-2);
for(int i=N-1;i>=1;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
void NTT(Poly &a,int len,int flg){
a.resize(len);//!!!
for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
w[0]=1; int G=flg==1?3:Pow(3,mod-2);
for(int i=2,l=1;i<=len;i<<=1,l<<=1){
wn=Pow(G,(mod-1)/i);
for(int k=l-2;k>=0;k-=2) w[k+1]=1ll*(w[k]=w[k>>1])*wn%mod;
for(int j=0;j<len;j+=i)
for(int k=j;k<j+l;k++){
int u=a[k],v=1ll*w[k-j]*a[k+l]%mod;
a[k]=add(u,v),a[k+l]=dec(u,v);
}
}
if(flg==-1) for(int i=0,Inv=Pow(len,mod-2);i<len;i++) a[i]=1ll*a[i]*Inv%mod;
}
Poly operator * (Poly a,Poly b){
int n=a.size(),m=b.size(),len=n+m-1;
if(n<20||m<20||len<200){
Poly c(len);
for(int i=0;i<n;i++) for(int j=0;j<m;j++)
c[i+j]=(c[i+j]+1ll*a[i]*b[j])%mod;
return c;
}
else{
int L=1;while(L<len) L<<=1;
for(int i=0;i<L;i++) r[i]=r[i>>1]>>1|(i&1?L>>1:0);
NTT(a,L,1),NTT(b,L,1);
for(int i=0;i<L;i++) a[i]=1ll*a[i]*b[i]%mod;
NTT(a,L,-1),a.resize(len);
return a;
}
}
Poly solve(int l,int r){
if(l==r) return {l,1};//tql! -std=c++0x
if((r-l+1)&1) return solve(l,r-1)*Poly{r,1};
int mid=(l+r)>>1,len=mid-l+2;
Poly A(solve(l,mid)),B(A),C(len);
for(int i=0,pw=1;i<len;i++,pw=1ll*pw*(len-1)%mod)
B[i]=1ll*B[i]*fac[i]%mod, C[len-i-1]=1ll*pw*inv[i]%mod;
B=B*C;
for(int i=0;i<len;i++) B[i]=1ll*B[i+len-1]*inv[i]%mod;
B.resize(len);
return A*B;
}
int main()
{
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
scanf("%d%d",&n,&m);
if(n==1) return puts("1"),0;
init();
Poly A=solve(2,n); int ans=0;
for(int i=1,lim=min(n,m);i<=lim;i++)
ans=(ans+1ll*C(m-1,i-1)*A[n-i])%mod;
printf("%d\n",ans);
}
T2:芬威克树
k进制树状数组的正确写法:
错误写法:
题解:
找到链首之后在动态开点线段树上插入,查询时区间查询即可。
Code:
#include<bits/stdc++.h>
#define maxn 200005
using namespace std;
char cb[1<<18],*cs,*cf;
#define getc() (cs==cf&&(cf=(cs=cb)+fread(cb,1,1<<18,stdin),cs==cf)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const int N = maxn*32;
int sum[N],lc[N],rc[N],tot,rt[N];
void insert(int &i,int l,int r,int x,int v){
if(!i) i=++tot; sum[i]^=v;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) insert(lc[i],l,mid,x,v);
else insert(rc[i],mid+1,r,x,v);
}
int query(int i,int l,int r,int x){
if(!i) return 0;
if(r<=x) return sum[i];
int mid=(l+r)>>1,ret=query(lc[i],l,mid,x);
if(x>mid) ret^=query(rc[i],mid+1,r,x);
return ret;
}
int n,m,k,p,W,pw[35],cnt,f[maxn][35],ct[maxn],inv[maxn];
map<int,int>id,val;
inline int lowbit(int x){int d=0;for(;!(x%k);x/=k) d++;return pw[d]*(x%k);}
inline int lowv(int x){for(;!(x%k);x/=k);return x%k;}
inline int lowp(int x){int d=0;for(;!(x%k);x/=k) d++;return d;}
inline int find(int x){
int t=lowp(x),s=lowv(x),d=t+1==W?0:x/pw[t+1];
for(int i=0;d;d>>=1,i++) if(d&1) s=inv[f[s][i]];
return id[f[s][0]*pw[t]];
}
void Add(int x,int v){
for(;ct[lowv(x)]<p&&x<=n;x+=lowbit(x)) val[x]^=v;
if(x>n) return;
insert(rt[find(x)],1,n,x,v);
}
int Qxor(int x){
if(ct[lowv(x)]<p) return val[x];
return query(rt[find(x)],1,n,x);
}
int main()
{
freopen("fenwick.in","r",stdin);
freopen("fenwick.out","w",stdout);
read(n),read(m),read(k);
int x,y,op;
if(k==2){
while(m--){
read(op),read(x);
if(op==1) read(y),insert(rt[0],1,n,x,y);
else printf("%d\n",query(rt[0],1,n,x));
}
return 0;
}
for(int i=1;i<=k;i++) for(int j=i;!(j&1);j>>=1) ct[i]++;
p=ct[k];
for(int i=n;i;i/=k) W++;
pw[0]=1; for(int i=1;i<W;i++) pw[i]=pw[i-1]*k;
for(int i=0;i<W;i++)
for(int j=0;;j++){
int x=(2*j+1)*(1<<p);
if(x>=k||1ll*x*pw[i]>n) break;
id[x*pw[i]]=++cnt;
for(int y=x;y<k;y<<=1) f[y][0]=x;
}
for(int i=1;i<k;i++) if(ct[i]>=p) inv[i*2%k]=i;
for(int j=1;j<=30;j++) for(int i=1;i<k;i++) if(ct[i]>=p) f[i][j]=f[inv[f[i][j-1]]][j-1];
while(m--){
read(op),read(x);
if(op==1) read(y),Add(x,y);
else{
int ans=0; for(;x;x-=lowbit(x)) ans^=Qxor(x);
printf("%d\n",ans);
}
}
}
T3:礼物
题解:
Burnside一波之后转化为求
[
1
,
N
]
[1,N]
[1,N]最长连续段不超过
M
M
M的方案数(1和
N
N
N视作连通),下面的分析截自BAJim_H
Code:
#include<bits/stdc++.h>
#define maxn 1000005
using namespace std;
const int mod = 998244353;
int T,n,m,k,phi[maxn],p[maxn/5],fac[maxn],inv[maxn];
bool v[maxn];
void Pre(const int N){
phi[1]=1;int m=0;
for(int i=2;i<=N;i++){
if(!v[i]) p[++m]=i,phi[i]=i-1;
for(int j=1,k;j<=m&&(k=i*p[j])<=N;j++){
v[k]=1; if(i%p[j]==0) {phi[k]=phi[i]*p[j];break;}
phi[k]=phi[i]*(p[j]-1);
}
}
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=N;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=N;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
}
inline int C(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
int main()
{
freopen("gift.in","r",stdin);
freopen("gift.out","w",stdout);
scanf("%d",&T);
Pre(maxn-5);
while(T--){
scanf("%d%d%d",&n,&m,&k);
int ans=0;
for(int d=1;d<=n;d++) if(n%d==0&&m%(n/d)==0){
int N=d,M=m/(n/d),ret=0;
if(N==M) ret=(k==n);
else{
for(int i=0,t=1,lim=min(N-M,M/(k+1));i<=lim;i++,t=-t)
ret=(ret+1ll*t*C(N-M,i)*C(N-1-i*(k+1),N-M-1))%mod;
ret=1ll*ret*N%mod*inv[N-M]%mod*fac[N-M-1]%mod;
}
ans=(ans+1ll*ret*phi[n/d])%mod;
}
printf("%d\n",(1ll*ans*inv[n]%mod*fac[n-1]%mod+mod)%mod);
}
}