190829 CSP-S 2019 模拟

A

求个异或前缀和然后做完全图最小异或生成树即可。

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
#define all(x) begin(x),end(x)
#define sz(x) (int)(x).size()
#define rsz resize
#define pb push_back
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int mod=998244353;
typedef long long ll;
inline int add(int a,int b){return (a+=b)<mod?a:a-mod;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Add(int&a,int b){(a+=b)<mod?a:(a-=mod);}
inline void Dec(int&a,int b){(a-=b)<0?(a+=mod):a;}
inline void Mul(int&a,int b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,Mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=4e6+5;
int n;
vector<int>a;
ll ans=0;
namespace trie{
	#define lc (son[p][0])
	#define rc (son[p][1])
	int P,son[N][2],tot,rt;
	inline void init(int dep){
		tot=1,rt=1;
		son[tot][0]=son[tot][1]=0;
		P=dep;
	}
	inline void insert(int x){
		for(ri t,i=P,p=rt;~i;--i){
			t=x>>i&1;
			if(!son[p][t]){
				son[p][t]=++tot;
				son[tot][0]=son[tot][1]=0;
			}
			p=son[p][t];
		}
	}
	inline int query(int x){
		int ret=0;
		for(ri t,i=P,p=rt;~i;--i){
			t=x>>i&1;
			if(son[p][t])p=son[p][t];
			else p=son[p][t^1],ret|=1<<i;
		}
		return ret;
	}
	#undef lc
	#undef rc
}
inline void solve(int dep,vector<int>a){
	if(a.size()<2||dep==-1)return;
	vector<int>tl,tr;
	for(ri i=sz(a)-1;~i;--i){
		if(a[i]>>dep&1)tr.push_back(a[i]);
		else tl.push_back(a[i]);
	}
	if(tl.size()&&tr.size()){
		int mn=0x3f3f3f3f;
		trie::init(dep);
		for(ri i=sz(tl)-1;~i;--i)trie::insert(tl[i]);
		for(ri i=sz(tr)-1;~i;--i)mn=min(mn,trie::query(tr[i]));
		ans+=mn;
	}
	solve(dep-1,tl),solve(dep-1,tr);
}
int main(){
	n=read();
	a.push_back(0);
	for(ri i=1;i<=n;++i)a.push_back(a.back()^read());
	solve(30,a);
	cout<<ans;
	return 0;
}

B

f i , j , k f_{i,j,k} fi,j,k表示 i i i 位整数模 K K K 余数为 j j j , 被限制的数字出现次数的三进制状态为 j j j 的方案数。
显然有递推式 f T , a , b = ∑ A + B = T , ( x ∗ 1 0 B + y ) % K = a , p ⊕ q = b f A , x , p ∗ f B , y , q f_{T,a,b}=\sum\limits_{A+B=T,(x*10^B+y)\%K=a,p\oplus q=b}f_{A,x,p}*f_{B,y,q} fT,a,b=A+B=T,(x10B+y)%K=a,pq=bfA,x,pfB,y,q
其中 ⊕ \oplus 表示三进制异或
这个时候可以通过倍增+ d p   g e t dp\ get dp get 到一个比较大众的部分分。然而我写了矩乘快速幂

然后考虑最后一维可以用 3 3 3 进制 F W T FWT FWT 搞一波,不会的戳这里这样你可以拿到一个不太大众的部分分

再观察第二个式子,哎哎这个不是一个 F F T FFT FFT 吗? 你稳了

好的恭喜你爆了标算。

对于两个矩阵,我们先把每一行用 3 3 3 进制 F W T FWT FWT 转点值, 然后用 F F T FFT FFT 把每一列多项式乘法, 然后再转回去即可。

别忘了处理那个10的幂

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int mod=998244353;
typedef long long ll;
inline int add(int a,int b){return (a+=b)<mod?a:a-mod;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Add(int&a,int b){(a+=b)<mod?a:(a-=mod);}
inline void Dec(int&a,int b){(a-=b)<0?(a+=mod):a;}
inline void Mul(int&a,int b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,Mul(a,a))if(p&1)Mul(ret,a);return ret;}
struct F{
	int x,y;
	F(int x=0,int y=0):x(x),y(y){}
	friend inline F operator+(F a,F b){return F(add(a.x,b.x),add(a.y,b.y));}
	friend inline F operator-(F a,F b){return F(dec(a.x,b.x),dec(a.y,b.y));}
	friend inline F operator*(F a,F b){return F(dec(mul(a.x,b.x),mul(a.y,b.y)),dec(add(mul(a.x,b.y),mul(a.y,b.x)),mul(b.y,a.y)));}
	friend inline void operator+=(F&a,F b){a=a+b;}
	friend inline void operator-=(F&a,F b){a=a-b;}
	friend inline void operator*=(F&a,F b){a=a*b;}
}w0,w1,w2,W1[21],W2[21],invv[21];
typedef vector<F> poly;
const int N=250,M=729;
ll T;
int K;
inline void FWT(poly&a,int n,int type){
	F a0,a1,a2;
	for(ri i=1;i<n;i*=3)for(ri j=0,len=i*3;j<n;j+=len)
	for(ri k=0;k<i;++k){
		a0=a[j+k],a1=a[j+k+i],a2=a[j+k+i*2];
		a[j+k]=a0+a1+a2;
		a[j+k+i]=a0+a1*w1+a2*w2;
		a[j+k+i*2]=a0+a1*w2+a2*w1;
		if(type==-1)swap(a[j+k+i],a[j+k+i*2]);
	}
	if(~type)return;
	F iv=F(ksm(n,mod-2),0);
	for(ri i=0;i<n;++i)a[i]*=iv;
}
vector<int>rev[21];
int lim=1,tim=0;
inline void init_ntt(){
	W1[20]=F(ksm(3,(mod-1)>>21),0);
	W2[20]=F(ksm(W1[20].x,mod-2),0);
	for(ri i=19;~i;--i)W1[i]=F(mul(W1[i+1].x,W1[i+1].x),0),W2[i]=F(mul(W2[i+1].x,W2[i+1].x),0);
	invv[0]=F(1,0);
	for(ri i=1,iv=mod+1>>1;i<21;++i)invv[i]=F(mul(invv[i-1].x,iv),0);
}
inline void init(const int&up){
	lim=1,tim=0;
	while(lim<up)lim<<=1,++tim;
	if(rev[tim].size())return;
	rev[tim].resize(lim);
	for(ri i=1;i<lim;++i)rev[tim][i]=(rev[tim][i>>1]>>1)|((i&1)<<(tim-1));
}
inline void ntt(poly&a,int type){
	for(ri i=0;i<lim;++i)if(i<rev[tim][i])swap(a[i],a[rev[tim][i]]);
	F a0,a1,mt,Mt;
	for(ri i=1,t=0;i<lim;i<<=1,++t)for(ri j=0,len=i<<1;j<lim;j+=len){
		mt=F(1,0),Mt=~type?W1[t]:W2[t];
		for(ri k=0;k<i;++k,mt*=Mt)a0=a[j+k],a1=a[j+k+i]*mt,a[j+k]=a0+a1,a[j+k+i]=a0-a1;
	}
	if(~type)return;
	for(ri i=0;i<lim;++i)a[i]*=invv[tim];
}
inline poly operator*(poly a,poly b){
	int n=a.size(),m=b.size(),t=n+m-1;
	if(t<=32){
		poly c(K);
		for(ri i=0;i<n;++i)for(ri j=0;j<m;++j)c[(i+j)%K]+=a[i]*b[j];
		return c;
	}
	init(t);
	a.resize(lim),ntt(a,1);
	b.resize(lim),ntt(b,1);
	for(ri i=0;i<lim;++i)a[i]*=b[i];
	ntt(a,-1);
	for(ri i=K;i<lim;++i)a[i%K]+=a[i];
	return a.resize(K),a;
}
inline poly mul(poly t,poly b,int md){
	poly a(t.size());
	for(ri i=0;i<K;++i)a[i*md%K]+=t[i];
	return a*b;
}
inline poly operator^(poly a,ll p){
	poly ret(K);
	ret[0]=F(1,0);
	for(int l=10%K;p;p>>=1,a=mul(a,a,l),(l*=l)%=K)if(p&1)ret=mul(ret,a,l);
	return ret;
}
char s[10];
int pw[10],vs[10],len;
poly f[N],a,res;
int main(){
	#ifdef ldxcaicai
	freopen("lx.in","r",stdin);
	#endif
	w0=F(1,0),w1=F(0,1),w2=F(mod-1,mod-1);
	init_ntt();
	cin>>T>>K;
	pw[0]=1;
	for(ri i=1;i<10;++i)pw[i]=pw[i-1]*3;
	scanf("%s",s+1),len=strlen(s+1);
	for(ri i=1;i<=len;++i)vs[s[i]-'0']=i;
	for(ri i=0;i<K;++i)f[i].resize(pw[len]);
	for(ri i=0;i<10;++i)++f[i%K][pw[vs[i]]/3].x;
	for(ri i=0;i<K;++i)FWT(f[i],pw[len],1);
	a.resize(K);
	res.resize(pw[len]);
	for(ri i=0;i<pw[len];++i){
		for(ri j=0;j<K;++j)a[j]=f[j][i];
		res[i]=(a^T)[0];
	}
	FWT(res,pw[len],-1);
	cout<<res[0].x;
	return 0;
}

C

最联赛的一道吧 毕竟A考的不是联赛知识点
我们先对 B B B 树做树哈希,然后枚举 A A A 树的根拿到 A A A 树上 D P DP DP
转移要用状压 d p dp dp ,注意要判断重复的子树QwQ
代码:

#include<bits/stdc++.h>
#define konjak_ldx 23112
#define ri register int
#define fi first
#define se secnod
#define lb lower_bound
#define ub upper_bound
#define all(x) begin(x),end(x)
#define sz(x) (int)(x).size()
#define rsz resize
#define pb push_back
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int mod=1e9+7;
typedef long long ll;
inline int add(int a,int b){return (a+=b)<mod?a:a-mod;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Add(int&a,int b){(a+=b)<mod?a:(a-=mod);}
inline void Dec(int&a,int b){(a-=b)<0?(a+=mod):a;}
inline void Mul(int&a,int b){a=(ll)a*b%mod;}
typedef long long ll;
const int N=2005,M=13;
int n,m,all,rt,rt1,rt2,msz,siz[M];
vector<int>e[N],g[M+5],h[M];
map<ll,int>S;
void getroot(int p,int fa){
	siz[p]=1;
	int ms=0;
	for(ri i=0,v;i<h[p].size();++i){
		if((v=h[p][i])==fa)continue;
		getroot(v,p),siz[p]+=siz[v],ms=max(ms,siz[v]);
	}
	ms=max(ms,all-siz[p]);
	if(ms<msz){
		rt1=p,rt2=0;
		msz=ms;
	}
	else if(ms==msz)rt2=p;
}
int vl[M],sig=0;
vector<int>Vl[M];
inline void gethash(int p,int fa){
	vector<int>t;
	for(ri i=0,v;i<g[p].size();++i){
		if((v=g[p][i])==fa)continue;
		gethash(v,p),t.push_back(vl[v]);
	}
	sort(all(t));
	ll ss=0;
	for(ri i=0;i<sz(t);++i)ss=ss*konjak_ldx+t[i];
	vl[p]=S.count(ss)?S[ss]:(Vl[S[ss]=++sig]=t,sig);
}
int tot=0,f[N<<3][M],vs[N][N];
bool vis[N<<3][M];
int DP(int p,int fa,int Tree){
	if(!vs[p][fa])vs[p][fa]=++tot;
	int id=vs[p][fa];
	if(vis[id][Tree])return f[id][Tree];
	vis[id][Tree]=1;
	int tim=Vl[Tree].size(),lim=1<<tim;
	vector<int>tmp(lim);
	tmp[0]=1;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa)continue;
		for(ri j=lim-1;~j;--j)if(tmp[j]){
			for(ri k=0;k<tim;++k){
				if(j>>k&1)continue;
				if((!k)||(Vl[Tree][k]!=Vl[Tree][k-1])||(j>>(k-1)&1)){
					Add(tmp[j|(1<<k)],mul(tmp[j],DP(v,p,Vl[Tree][k])));
				}
			}
		}
	}
	return f[id][Tree]=tmp[lim-1];
}
int main(){
	#ifdef ldxcaicai
	freopen("lx.in","r",stdin);
	#endif
	n=read();
	for(ri i=1,u,v;i<n;++i){
		u=read(),v=read();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	m=read();
	for(ri i=1,u,v;i<m;++i){
		u=read(),v=read();
		h[u].push_back(v);
		h[v].push_back(u);
	}
	all=msz=m,rt1=rt2=0,getroot(1,0);
	int ans=0;
	if(rt2){
		++m;
		rt=m;
		for(ri u=1,v;u<m;++u){
			for(ri i=0;i<h[u].size();++i){
				v=h[u][i];
				if((u==rt1&&v==rt2)||(u==rt2&&v==rt1)){
					g[m].push_back(u);
					g[u].push_back(m);
					continue;
				}
				g[u].push_back(v);
			}
		}
		gethash(rt,0);
		for(ri u=1,v;u<=n;++u){
			for(ri i=0;i<e[u].size();++i){
				v=e[u][i];
				if(u>v)continue;
				Add(ans,mul(DP(u,v,Vl[vl[rt]][0]),DP(v,u,Vl[vl[rt]][1])));
			}
		}
	}
	else{
		rt=rt1;
		for(ri u=1;u<=m;++u)g[u]=h[u];
		gethash(rt,0);
		for(ri i=1;i<=n;++i)Add(ans,DP(i,0,vl[rt]));
	}
	cout<<ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值