CSP2024 前集訓:多校A層衝刺NOIP2024模擬賽05

卡布叻_周深發表於2024-10-12

前言

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 連通子樹與樹的重心

  • 弱化版:P4582 [FJOI2014] 樹的重心

先求出原樹的重心,可能有一個或兩個,分類討論。

兩個重心

這個比較容易處理,設兩個重心分別為 \(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("");
}

相關文章