CSP2024 前集训:多校A层冲刺NOIP2024模拟赛05

前言

image

一次模拟赛四个计数,后三道一个 trick,yl 他妈都能被气活:

T2、T3、T4 全是正难则反的 trick,T3、T4 都需要回退背包,T1 也可以背包。

T2 没想到正难则反一直死磕心态炸了,其实要试看看 T3 搞不好还想出来了。

以后再想不到正难则反记得抽死我谢谢。

T1 好数

可以直接暴力背包套 bitset 操过去,复杂度 \(O(\dfrac{nv}{w})\)\(v\) 是值域。

也可以移项 \(a+b+c=d\to a+b=d-c\) 开桶 \(O(n^2)\) 维护。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=5010,M=6e5+10,V=1e5;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,ans,a[N]; bitset<M>f[5];
signed main()
{
	freopen("number.in","r",stdin),freopen("number.out","w",stdout);
	read(n); for(int i=1;i<=n;i++) read(a[i]),a[i]+=V;
	f[0][0]=1; for(int i=1;i<=n;i++)
	{
		ans+=f[3][a[i]+2*V];
		for(int j=1;j<=3;j++) f[j]|=(f[j-1]<<a[i]);
	}
	write(ans);
}

T2 SOS字符串

正难则反,\(26^n\) 减去 SOS 个数 \(\le 2\) 的,DP 直接转移即可。

赛时一直正着推,没想到就算 SOS 数量固定也会有算重的,遂 \(n\le 12\) 都是错的。

所以以后先把暴力打出来方便拍,还有组合数搞不出来的时候想一想 DP。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e6+10,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,ans=1,f[2][3][3];
signed main()
{
	freopen("sos.in","r",stdin),freopen("sos.out","w",stdout);
	read(n); f[0][0][0]=1;
	for(int i=1,x=1,y=0;i<=n;i++,x^=1,y^=1,ans=26ll*ans%P) 
		for(int j=0;j<=2;j++)
		{
			f[x][j][0]=25ll*(f[y][j][0]+f[y][j][2])%P;
			(f[x][j][0]+=24ll*f[y][j][1]%P)%=P;
			if(j) (f[x][j][0]+=f[y][j-1][2])%=P;
			f[x][j][1]=(f[y][j][0]+f[y][j][1])%P;
			f[x][j][2]=f[y][j][1];
		}
	for(int i=0,x=n&1;i<=2;i++) 
		for(int j=0;j<=2;j++) (ans+=P-f[x][i][j])%=P;
	write(ans);
}

T3 集训营的气球

一样的 trick 正难则反,然后就是回退背包板子。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e6+10,M=20,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,c,m,a[N],b[N]; ll ans=1,f[M];
int inv(ll a,int b=P-2)
{ll res=1; for(;b;(a*=a)%=P,b>>=1) if(b&1) (res*=a)%=P; return res;}
signed main()
{
	freopen("balloon.in","r",stdin),freopen("balloon.out","w",stdout);
	read(n,c); f[0]=1;
	for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++) read(b[i]);
	for(int i=1;i<=n;(f[0]=f[0]*b[i]%P)%=P,(ans*=a[i]+b[i])%=P,i++) 
		for(int j=c-1;j>=1;j--) f[j]=(f[j-1]*a[i]%P+f[j]*b[i]%P)%P;
	read(m); for(int i=1,o,x,y,sum,va,vb;i<=m;i++) 
	{
		read(o,x,y); va=inv(a[o]),vb=inv(b[o]),sum=0;
		(ans*=1ll*inv(a[o]+b[o])*(x+y)%P)%=P,(f[0]*=vb)%=P;
		for(int j=1;j<c;j++) f[j]=(f[j]-f[j-1]*a[o]%P+P)*vb%P;
		for(int j=c-1;j>=1;j--) (sum+=(f[j]=(f[j-1]*x%P+f[j]*y%P)%P))%=P;
		a[o]=x,b[o]=y; write((ans-sum-((f[0]*=y)%=P)+(P<<1))%P),puts("");
	}
}

T4 连通子树与树的重心

先求出原树的重心,可能有一个或两个,分类讨论。

两个重心

这个比较容易处理,设两个重心分别为 \(u,v\),必定存在边 \((u,v)\),从而分别以两个重心为根跑一遍树形背包。

\(f_{x,i}\) 表示以 \(x\) 为根且块内节点数量为 \(i\) 的连通块的个数,有转移:

\[∀i:[1,sz_x],f_{x,i}=\sum_{y\in son_x}\sum_{j=1}^{\min(i-1,sz[y])}f_{y,j}\times f_{x,i-j} \]

最后答案为 \(\sum\limits_{i=1}^{\max(sz_u,sz_v)}f_{u,i}\times f_{v,i}\)

  • 关于本题树形背包的复杂度:

    转移等价于找出一组 \(x,y\)\(lca(x,y)\) 做出贡献,遂每组 \(x,y\) 只可能被记到一次,遂复杂度为 \(O(n^2)\)

一个重心

先以重心为根跑一遍上面的 DP,然后考虑容斥,依然是正难则反。

求出 \(f_{x,i}\) 中最大子树 \(>\dfrac{i}{2}\) 的即可,回退背包。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define pb push_back
using namespace std;
const int N=5010,P=10007;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,ans,rt[3],sz[N],mx[N],f[N][N],g[N][N];
vector<int>e[N];
void getrt(int x,int t)
{
	sz[x]=1; for(int y:e[x]) if(y!=t)
		getrt(y,x),sz[x]+=sz[y],mx[x]=max(mx[x],sz[y]);
	mx[x]=max(mx[x],n-sz[x]); if(mx[x]<=(n>>1)) rt[++rt[0]]=x;
}
void dfs(int x,int t)
{
	sz[x]=1,f[x][0]=f[x][1]=1; for(int y:e[x]) if(y!=t)
	{
		dfs(y,x),sz[x]+=sz[y];
		for(int i=sz[x];i;i--) for(int j=1;j<=min(sz[y],i-1);j++)
			(f[x][i]+=f[x][i-j]*f[y][j]%P)%=P;
	}
}
signed main()
{
	freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
	read(n); for(int i=1,x,y;i<n;i++) read(x,y),e[x].pb(y),e[y].pb(x);
	getrt(1,0); if(!(rt[0]&1))
	{
		cerr<<1<<endl;
		dfs(rt[1],rt[2]),dfs(rt[2],rt[1]);
		for(int i=1;i<=n;i++) (ans+=f[rt[1]][i]*f[rt[2]][i]%P)%=P;
		return write(ans),puts(""),0;
	}
	int x=rt[1]; dfs(x,0); for(int i=1;i<=n;i++) (ans+=f[x][i])%=P;
	for(int y:e[x]) for(int i=1;i<=n;i++)
	{
		g[y][i]=f[x][i]; for(int j=1;j<=min(i,sz[y]);j++)
			(g[y][i]+=P-g[y][i-j]*f[y][j]%P)%=P;
	}
	for(int y:e[x]) for(int i=1;i<=n;i++)
		for(int j=(i+1)/2;j<=min(i,sz[y]);j++)
			(ans+=P-f[y][j]*g[y][i-j]%P)%=P;
	write(ans),puts("");
}
posted @ 2024-10-12 06:45  卡布叻_周深  阅读(28)  评论(0编辑  收藏  举报