模拟赛20200219【染色覆盖(DP+卡常NTT),k进制lowbit,最长连续段限制环染色(Burnside)】

13 篇文章 0 订阅
9 篇文章 0 订阅
T1:染色问题

对摆在玩家面前的一排初始无色的格子进行染色,每次染色可以将连续一段格子染成一种之前没有出现过的颜色,要求在所有操作后每一个格子都被染了至少一次色(重复染色会使前一次染的颜色被覆盖)
给出长度 n n n和染色次数 m m m,求最终可能的颜色序列种数。
n , m ≤ 1 0 6 n,m\le10^6 n,m106

题解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上述的倍增FFT的具体实现方式是递归 [ l , r ] [l,r] [l,r],如果 r − l + 1 r-l+1 rl+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=0naij=0i(ji)xjmidij=j=0nxji=jnai(ji)midij
记新多项式为 B ( x ) B(x) B(x),令 A i = a i ∗ i ! A_i=a_i*i! Ai=aii! 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} Bjj!=i=jnAiCij
C C C反转,记 D i = C n − i D_i=C_{n-i} Di=Cni,则 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)} Bjj!=Pn+j=i=jnAiDn(ij)

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);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值