多校A层冲刺NOIP2024模拟赛05

好数(number

没啥好说的直接 \(O(n^2)\) 枚举即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=2e6+107;
const int d=2e5;
int n,a[N],sum[N];

int read()
{
	int f=1,s=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return f*s;
}



signed main()
{
	freopen("number.in","r",stdin);
	freopen("number.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i-1;j++)
		{
			int x=a[i]-a[j]+d;
			if(sum[x]==1)
			{
				ans++;
				break;
			}	
		}
		for(int j=1;j<=i;j++) sum[a[i]+a[j]+d]=1;
	}
	printf("%d",ans);
}

SOS字符串(sos)

可DP,可分讨,我写的分讨,其实DP也是那个流程。如果DP,要正难则反,直接统计小于3个的情况,最后用 \(26^n\) 减去即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=2e6+107;
const int mod=1e9+7;
int n,ans;

int f[N][10];

int read()
{
	int f=1,s=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return f*s;
}

int dfs(int len,int k)
{
	if(f[len][k]!=-1) return f[len][k];
	if(len==0) 
	{
		if(k==0) return f[len][k]=1;
		else return f[len][k]=0;
	}
	
	int res=0;
	if(k==9) // 9: ' *SOS*SOS*SOS* '
	{
		(res+=dfs(len-1,8))%=mod;//*SOS*SOS*SO + S
		(res+=dfs(len-1,9)*26)%=mod;//*SOS*SOS*SOS* + A-Z
	}
	if(k==8)// 8: ' *SOS*SOS*SO '
	{
		(res+=dfs(len-1,7))%=mod;//*SOS*SOS*S + O
	}
	if(k==7)// 7: ' *SOS*SOS*S '
	{
		(res+=dfs(len-1,6))%=mod;//*SOS*SOS* + S
		(res+=dfs(len-1,7))%=mod;//*SOS*SOS(*S) + S
	}
	if(k==6)// 6: ' *SOS*SOS* '
	{
		(res+=dfs(len-1,5))%=mod;//*SOS*SO + S
		(res+=dfs(len-1,6)*25)%=mod;//*SOS*SOS* + A-Z - S
		(res+=dfs(len-1,7)*24)%=mod;//*SOS*SOS(*S) + A-Z -S(k==7) -O(k==8)
		(res+=dfs(len-1,8)*25)%=mod;//*SOS*SOS(*SO) + A-Z - S(k==9)
	}
	if(k==5)// 5: ' *SOS*SO '
	{
		(res+=dfs(len-1,4))%=mod;//*SOS*S + O
	}
	if(k==4)// 4: ' *SOS*S '
	{
		(res+=dfs(len-1,3))%=mod;//*SOS* + S
		(res+=dfs(len-1,4))%=mod;//*SOS(*S) + S
	}
	if(k==3)// 3: ' *SOS* '
	{
		(res+=dfs(len-1,2))%=mod;//*SO + S
		(res+=dfs(len-1,3)*25)%=mod;//*SOS* + A-Z -S(k==4) 
		(res+=dfs(len-1,4)*24)%=mod;//*SOS*S + A-Z -S(k==4) -O(k==5)
		(res+=dfs(len-1,5)*25)%=mod;//*SOS*SO + A-Z -S(k==6)
	}
	if(k==2)// 2: ' *SO '
	{
		(res+=dfs(len-1,1))%=mod;//*S + O
	}
	if(k==1)// 1: ' *S '
	{
		(res+=dfs(len-1,0))%=mod;//* + S
		(res+=dfs(len-1,1))%=mod;//(*S) + S
	}
	if(k==0)// 0: ' * '
	{
		(res+=dfs(len-1,0)*25)%=mod;//* + A-Z - S(k==1)
		(res+=dfs(len-1,1)*24)%=mod;//* + A-Z - S(k==1) - O(k==2)
		(res+=dfs(len-1,2)*25)%=mod;//* + A-Z -S(k==3)
	}
	return f[len][k]=res%mod;
}
// 0: ' * '
// 1: ' *S '
// 2: ' *SO '
// 3: ' *SOS* '
// 4: ' *SOS*S '
// 5: ' *SOS*SO '
// 6: ' *SOS*SOS* '
// 7: ' *SOS*SOS*S '
// 8: ' *SOS*SOS*SO '
// 9: ' *SOS*SOS*SOS* '
int res[N];
signed main()
{
	freopen("sos.in","r",stdin);
	freopen("sos.out","w",stdout);
	n=read();
	memset(f,-1,sizeof f);
	dfs(n,9);
	printf("%lld\n",f[n][9]%mod);
	return 0;
}

集训营的气球(balloon)

正难则反,首先我们可以想到直接跑01背包,然后统计小于 \(c\) 的情况,复杂度是 \(nc\) ,关键是修改操作,如何动态修改,这里有一个新的技巧:退背包(可能是我太菜了,第一次听到),每次修改的时候先退背包,再正常跑一遍的DP,然后就没啥了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=2e6+107;
const int p=1e9+7;
int n,c,q;
int a[N],b[N];
int f[N],sum;

int read()
{
	int f=1,s=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return f*s;
}

int qpow(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b=b>>1;
	}
	return ans;
}


signed main()
{
	freopen("balloon.in","r",stdin);
	freopen("balloon.out","w",stdout);
	n=read(),c=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) b[i]=read();
	f[0]=1,sum=1;
	for(int i=1;i<=n;i++) 
	{
		sum=sum*(a[i]+b[i])%p;
		for(int j=c-1;j>=1;j--)
		{
			f[j]=(f[j-1]*a[i]%p+f[j]*b[i]%p)%p;
		}
		f[0]=f[0]*b[i]%p;
	}
	int q=read();
	for(int i=1;i<=q;i++)
	{
		int pos=read(),x=read(),y=read();
		sum=sum*qpow(a[pos]+b[pos],p-2)%p*(x+y)%p;
		int inv=qpow(b[pos],p-2);
		f[0]=f[0]*inv%p;
		for(int j=1;j<=c-1;j++) f[j]=(f[j]-f[j-1]*a[pos]%p+p)*inv%p;
		for(int j=c-1;j>=1;j--) f[j]=(f[j-1]*x%p+f[j]*y%p)%p;
		f[0]=f[0]*y%p;
		int res=sum;
		for(int j=0;j<c;j++) res=(res-f[j]+p)%p;
		a[pos]=x,b[pos]=y;
		printf("%lld\n",res);
	}
}

连通子树与树的重心(tree)

先求出树的重心,这里是会有两种情况:一个重心和两个重心。

我们先来设计一下DP,设 \(f[i][j]\) 表示以 \(i\) 为根的子树选了 \(j\) 个节点相互连通的方案数。其中这个 \(f\) ,我们可以通过树形背包DP来得到。\(f[u][i]+=f[v][j]*f[u][i-j]\)

那先来看有两个重心的情况,分析一下,首先连接两个重心的边肯定不能断,且两个重心除了相互连通外要有同样个数的节点与它相连。

那比较好解决,我们直接断边每个重心跑一遍DP,最后答案为 \(\sum_{i=1}^{n}f[c1][i]*f[c2][i]\)

接着来分析只有一个重心的时候,比较好像到的是,再跑一遍DP,我们设 \(g[i][j]\) 表示以 \(c\) 为根,大小为 \(i\) 的一个连通块中子树大小为 \(j\) 的方案数。由重心的性质:重心所有的子树大小不超过 \(n/2\) 可知,最后答案应为 \(\sum_{i,j*2<=i}\)

但这样的复杂度为 \(n^3\) 无法接受,我们考虑换个方法,考虑正难则反(真服了,一场模拟赛放三这玩意),我们只需要用总方案减去那些子树节点大于 \(i/2\) 的情况。如果我们直接用 \(f\) 数组很明显不对,因为如果我们制定任意一个子树 \(v\) 的节点个数大于 \(i/2\)\(j\) ,那此时 \(f[u][i-j]\) 因为考虑了 \(v\) 这个子树而会算重。这里我们就需要容斥一下,还是考虑退背包操作(这场模拟赛没话说),这个直接根据动态转移方程推一下,还是比较好说的,剩下的就没了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=5000+107;
const int mod=10007;
int n;
int f[N][N],g[N][N];

int read()
{
	int f=1,s=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return f*s;
}

int h[N<<3],to[N<<3],nxt[N<<3],tot;
void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=h[x];
	h[x]=tot;
}

int sz[N],w[N],c[3],cnt;
void dfs(int u,int fa)
{
	sz[u]=1,w[u]=0;
	for(int i=h[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa)
		{
			dfs(v,u);
			sz[u]+=sz[v];
			w[u]=max(w[u],sz[v]);
		}
	}
	w[u]=max(w[u],n-sz[u]);
	if(w[u]<=n/2) c[cnt++]=u;
}

void dp(int u,int fa)
{
	sz[u]=1;
	f[u][0]=f[u][1]=1;
	for(int x=h[u];x;x=nxt[x])
	{
		int v=to[x];
		if(v==fa) continue;
		dp(v,u);
		sz[u]+=sz[v];
		for(int i=sz[u];i>=0;i--)
		{
			for(int j=1;j<=sz[v]&&j<=i-1;j++)
			{
				(f[u][i]+=f[v][j]*f[u][i-j])%=mod;
			}
		}
	}
}

void solve2()
{
	
	dp(c[0],c[1]);
	dp(c[1],c[0]);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		(ans+=f[c[0]][i]*f[c[1]][i]%mod)%=mod;
	}
	printf("%lld",ans);
}

void solve1()
{
	
	int u=c[0],ans=0;
	dp(u,0);
	for(int i=1;i<=n;i++) (ans+=f[u][i])%=mod;
	for(int i=h[u];i;i=nxt[i])
	{
		int v=to[i];
		for(int j=1;j<=n;j++)
		{
			g[v][j]=f[u][j];
			for(int k=1;k<=min(j,sz[v]);k++)
			{
				g[v][j]=(g[v][j]-g[v][j-k]*f[v][k]%mod+mod)%mod;
			}
		}
	}
		
	for(int i=1;i<=n;i++)
	{
		for(int j=h[u];j;j=nxt[j])
		{
			int v=to[j];
			for(int k=(i+1)/2;k<=i&&k<=sz[v];k++)
			{
				ans=(ans-f[v][k]*g[v][i-k]%mod+mod)%mod;
			}
		}
	}
	printf("%lld\n",ans);
}

signed main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();
	for(int i=1;i<=n-1;i++)
	{
		int x=read(),y=read();
		add(x,y),add(y,x);
	}
	dfs(1,0);
	if(c[1]) solve2();
	else solve1();
	return 0;
}
posted @ 2024-10-12 20:13  zhengchenxi  阅读(11)  评论(0编辑  收藏  举报