7.19考试总结(NOIP模拟20)[玩具·y·z]

与其被自己的本性牵着走而痛苦,倒不如试着改变自己。

前言

首先自我检讨一下,T1的部分分是原题,但是我这个 FW 居然没有看出来。。(主要是我看错题了)

论语文素养如何限制 OI 水平 别人眼里是一棵树,我眼里是一堆柱子。

T1 玩具

解题思路

预处理 dp[i][j] 表示 i 个点的森林,有 j 个点在第一棵树的概率,转移的时候考虑第 i 个点是否在第一棵子树中。

可以得到状态转移方程:\(dp_{i,j}=dp_{i-1,j-1}\times (j-1)\times inv_i+dp_{i-1,j}\times (i-j)\times inv_i\)

然后用 f[i][j]g[i][j] 分别表示有 i 个点的树或者森林深度不超过 j 的概率。

那么 \(f_{i,j}\) 可以直接从 \(g_{i-1,j-1}\) 转移过来,可以理解为,把森林中所有树的根节点都连为 新加入节点的子节点就好了。

对于 g 数组的转移考虑从一棵树和一个森林中转移:

\[g_{i,j}=\sum\limits_{k=1}^i f_{k,j}\times g_{i-k,j}\times dp_{i,k} \]

然后在处理时候注意边界问题,以及用之前的状态更新以后的就好了。

code

70pts

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=210;
int n,mod,ans,base=1,f[N][N],c[N][N];
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
void get_C()
{
	for(int i=0;i<N;i++)
	{
		c[i][0]=c[i][i]=1;
		for(int j=1;j<i;j++)
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	}
}
int ksm(int a,int b)
{
    int answer=1;
    while(b)
    {
        if(b&1)
        	answer=(a*answer)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return answer%mod;
}
signed main()
{
	n=read();
	mod=read();
	get_C();
	f[1][1]=f[2][2]=1;
	for(int i=3;i<=n;i++)
		for(int j=2;j<=n;j++)
		{
			for(int p=1;p<=i-2;p++)
				for(int q=1;q<=min(j-2,p);q++)
					f[i][j]=(f[i][j]+f[p][q]*f[i-p][j]%mod*c[i-2][p-1]%mod)%mod;
			for(int p=1;p<=i-1;p++)
				for(int q=1;q<=j;q++)
					f[i][j]=(f[i][j]+f[p][j-1]*f[i-p][q]%mod*c[i-2][p-1]%mod)%mod;
		}
	for(int i=1;i<n;i++)
		base=base*i%mod;
	for(int i=2;i<=n;i++)
		ans=(ans+(i-1)*f[n][i]%mod)%mod;
//	cout<<ans<<' '<<base<<endl;
	printf("%lld",ans*ksm(base,mod-2)%mod);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=210;
int n,mod,ans,inv[N],f[N][N],dp[N][N],g[N][N];
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
void get_inv()
{
	inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)
        inv[i]=((mod-mod/i)*inv[mod%i])%mod;
}
signed main()
{
	n=read();
	mod=read();
	get_inv();
	dp[1][1]=1;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i;j++)
			dp[i][j]=(dp[i-1][j-1]*(j-1)%mod*inv[i]%mod+dp[i-1][j]*(i-j)%mod*inv[i]%mod)%mod;
	for(int i=0;i<=n;i++)
		f[1][i]=g[0][i]=1;
	for(int i=1;i<=n;i++)
		for(int j=0;j<i;j++)
		{
			g[i][j]=0;
			for(int k=1;k<=i;k++)
				g[i][j]=(g[i][j]+f[k][j]*g[i-k][j]%mod*dp[i][k]%mod)%mod;
//			cout<<g[i][j]<<endl;
			f[i+1][j+1]=g[i][j];
			for(int k=j+1;k<=n;k++)
				f[i+1][k]=f[i+1][j+1];
			for(int k=i;k<=n;k++)
				g[i][k]=g[i][j];
		}
	for(int i=2;i<=n;i++)
		ans=(ans+(i-1)*(f[n][i-1]-f[n][i-2]+mod)%mod)%mod;
	printf("%lld",ans);
	return 0;
}

T2 y

解题思路

这里借鉴了一下 cty dalao的做法。

因为空间时间卡的都比较紧,因此我们考虑把距离为 d 的路径掰成两半来考虑

f[i][j][sta]表示走了 i 步,到了 j 点状态为 sta 的情况是否存在。

首先考虑前 \(\dfrac{d}{2}\) 的,因为只能从 1 节点出发,所以边界只能是 \(f_{0,1,0}=true\)

然后就是枚举顺序了,不难发现第一层一定是步数(毕竟我们的 DP 方程都是从前一步转移过来的吗)

接下来点以及它所联通的边,进而枚举每一步状态(注意这里应该是 |而不是直接的=,到达这种状态的方式不只有一种)

对于这一次的每一个结尾点,将它可以有的状态压进一个 vector 数组。

剩下的 \(\dfrac{d}{2}\) 与上面的类似,但是要反着,从 n 个结尾点向前进行更新。

因此,起始点就可以是任意一个点了,所以边界就要把所有的 \(f_{0,i,0}\) 都赋值为 true。

并且和上一次一样把状态压入一个 vector 。

接下来就比较简单了,就是把两段路径合并了,这里需要记录一下。

因为对于两段路径的计算是分离的,并且我们仅仅想要路径的种类数,因此状压的状态的正反无需考虑。

代码的实现细节诶不是很多,注意压 vector 的时候不要写进大循环里,不然就会 TLE ,单独拎出来处理就好了。

code

#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=100,M=N*(N-1);
int n,m,dist,ans;
int tot,head[N],nxt[M<<1],ver[M<<1],edge[M<<1];
bool f[15][N][1<<12],f2[15][N][1<<12],vis[1<<22];
vector<int> v[N],v2[N];
void add_edge(int x,int y,int val)
{
	ver[++tot]=y;
	edge[tot]=val;
	nxt[tot]=head[x];
	head[x]=tot;
}
signed main()
{
//	freopen("date.in","r",stdin);
	n=read();
	m=read();
	dist=read();
	for(int i=1,x,y,val;i<=m;i++)
	{
		x=read();
		y=read();
		val=read();
		add_edge(x,y,val);
		add_edge(y,x,val);
	}
	f[0][1][0]=true;
	for(int k=0;k<(dist+1)/2;k++)
		for(int i=1;i<=n;i++)
		{
			for(int j=head[i];j;j=nxt[j])
			{
				int to=ver[j],col=edge[j];
				for(int sta=0;sta<(1<<((dist+1)/2));sta++)
				{
					f[k+1][to][sta<<1|col]|=f[k][i][sta];
				//	if(f[k+1][to][sta<<1|col])
				//		cout<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
				//		cout<<"F1:	"<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
				}
			}
		}
	for(int i=1;i<=n;i++)
		for(int sta=0;sta<(1<<((dist+1)/2));sta++)
			if(f[(dist+1)/2][i][sta])
				v[i].push_back(sta);
	for(int i=1;i<=n;i++)
		f2[0][i][0]=true;
	for(int k=0;k<dist-(dist+1)/2;k++)
		for(int i=1;i<=n;i++)
		{
			for(int j=head[i];j;j=nxt[j])
			{
				int to=ver[j],col=edge[j];
				for(int sta=0;sta<(1<<(dist-(dist+1)/2));sta++)
				{
					f2[k+1][to][sta<<1|col]|=f2[k][i][sta];
				//	if(f2[k+1][to][sta<<1|col])	f();
				//	if(f2[k+1][to][sta<<1|col])
				//		cout<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
				//		cout<<"F2:	"<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
				}
			}
		}
	for(int i=1;i<=n;i++)
		for(int sta=0;sta<(1<<(dist-(dist+1)/2));sta++)
			if(f2[dist-(dist+1)/2][i][sta])
				v2[i].push_back(sta);
	for(int k=1;k<=n;k++)
	{
		for(int i=0;i<v[k].size();i++)
		{
			for(int j=0;j<v2[k].size();j++)
			{
				vis[v[k][i]<<(dist-(dist+1)/2)|v2[k][j]]=true;
			}
		}
	}
	for(int i=0;i<(1<<dist);i++)
		ans+=vis[i];
	printf("%lld",ans);
	return 0;
}

T3 z

大坑未补

posted @ 2021-07-20 11:20  Varuxn  阅读(64)  评论(0编辑  收藏  举报