1. 程式人生 > 其它 >noip模擬測試20

noip模擬測試20

考試總結:這次考試,我非常真實地感覺到了自己能力的提高,具體來說,在之前的考試中,讀完題之後我只會想到暴力的思路,甚至有的題連暴力都打不出來,但是這次在考場上我已經有了自己的一些想法,有了一個深入思考的過程,自己演算,推式子也寫了幾張草稿紙,對於測試點的部分分也有了一定的把握。在改題的時候,我基本上都是研究下發的題解自己該出來的,總之,這幾次考試我的收穫很大,所有的付出都是值得的。

T1 玩具

思路:我們模擬一下操作過程,那麼很顯然,最終得到的應該是一顆樹,那麼如果我們不看 1 結點,那麼應該是一個森林,這道題是個 dp,我們設i個點的森林,有j個點在第一顆樹的概率\(dp_{i,j}=dp_{i-1,j-1}\times(j-1)\times(inv_i)+dp_{i-1,j}\times(i-j)\times(inv_i)\)

設第一棵樹有i 個點,深度不超過j的概率 \(f_{i,j}=g_{i-1,j-1}\),設有i個點的森林,深度不超過j的概率 \(g_{i,j}=\sum\limits_{k=1}^{i}f_{k,j}\times g_{i-k,j}\times {dp_{i,k}}\),這個方程式比較好理解,可以結合畫圖理解,那麼注意細節問題就是在轉移的時候,我們可以用刷表法,即用當前的更新後面的,還要注意當前列舉不到的狀態要在當前轉移過去v,具體實現見程式碼:

AC_Code


#include<bits/stdc++.h>
#define int long long
#define re register int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=210;
long long n,p,ans;
int inv[N],g[N][N],f[N][N],dp[N][N];
ii read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return (f)?x:(-x);
}
#undef int
int main()
{
	/*
	i個點的森林,有j個點在第一顆樹的概率
	dp[i][j]=dp[i-1][j-1]*(j-1)*inv[i]+dp[i-1][j]*(i-j)*inv[i];//拿出j-1個,加上拿另外的 i-j個
	i個點的樹,深度不超過j的概率
	f[i][j]=g[i-1][j-1]//畫圖顯然
	i個點的森林,深度不超過j的概率
	g[i][j]=sum[k=1 ->i] f[k][j]*g[i-k][j]*dp[i][k]; 
	 */
	#define int long long
	n=read();
	p=read();
	inv[1]=1;
    for(re i=2;i<=n;i++)
    {
    	inv[i]=(p-(p/i))*inv[p%i]%p;
    }
	dp[1][1]=1;
	for(re i=2;i<=n;i++)
	{
		for(re j=1;j<=i;j++)
		{
			dp[i][j]=(dp[i-1][j-1]%p*(j-1)%p*inv[i]+dp[i-1][j]%p*(i-j)%p*inv[i])%p;
		}
	}
	for(re i=0;i<=n;i++)
	{
		g[0][i]=1;
		f[1][i]=1;
	}
	for(re i=1;i<=n;i++)
	{
		for(re j=0;j<i;j++)
		{
			for(re k=1;k<=i;k++)
				g[i][j]=(g[i][j]%p+f[k][j]%p*g[i-k][j]%p*dp[i][k]%p+p)%p;
			f[i+1][j+1]=g[i][j];
			for(re k=j+1;k<=n;k++)
				f[i+1][k]=f[i+1][j+1];
		}
		for(re k=i;k<=n;k++)
			g[i][k]=g[i][i-1];
	}
	for(re i=1;i<n;i++)
		ans=(ans%p+i*(f[n][i]-f[n][i-1]+p)%p)%p;
	printf("%lld\n",ans);
	return 0;
}

T2 y

思路:顯然是一個狀壓dp,我們設 \(f_{i,j,s}\)表示當前走了 i 步,終點為 j,當前狀態為 S 是否可以達到,
那麼\(f_{i+1,to[j],s<<1|val[k]}|=f_{i,j,s}\),因為時間和空間的限制,我們直接列舉所有點轉移會 T飛,所以我們考慮列舉路徑的中點,正著作一遍dp,倒著做一遍dp,同時記錄每個點作為中點時的狀態,最後輸出即可。
注意細節:我們儲存狀態的時候一定要最後單獨開一個for迴圈,不要在計算的過程中儲存,否則直接T飛

AC_Code
#include<bits/stdc++.h>
#define re register int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
#define ii inline int
#define iv inline void
#define head heaaaddddd
#define next netyurywi
using namespace std;
const int N=1e4+10;
int n,m,d,tot;
long long ans=0;
int to[N<<1],next[N<<1],head[N],val[N<<1];
int f[12][95][1<<12], f2[12][95][1<<12];
vector<int> v1[95],v2[95];
bool vis[1<<22];
ii read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return (f)?x:(-x);
}
iv add(int x,int y,int z)
{
	to[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
	val[tot]=z;
}	
int main()
{
	n=read();
	m=read();
	d=read();
	int a,b,c;
	for(re i=1;i<=m;i++)
	{
		a=read();
		b=read();
		c=read();
		add(a,b,c);
		add(b,a,c);
	}
	int	d1=(d>>1);
	int	d2=d-d1;
	f[0][1][0]=1;
	for(re i=0;i<d1;i++)
	{
		for(re j=1;j<=n;j++)
		{
			for(re k=head[j];k;k=next[k])
			{
				int p=to[k];
				for(re s=0;s<(1<<i);s++)
				{
					f[i+1][p][s<<1|val[k]]|=f[i][j][s];
				}	
			}
		}
	}
	
	for(re i=1;i<=n;i++)
		f2[0][i][0]=1;
	for(re i=0;i<d2;i++)
	{
		for(re j=1;j<=n;j++)
		{
			for(re k=head[j];k;k=next[k])
			{
				int p=to[k];
				for(re s=0;s<(1<<i);s++)
				{
					f2[i+1][p][s<<1|val[k]]|=f2[i][j][s];
				}
			}
		}
	}
	for(re i=1;i<=n;i++)
	{
		for(re j = 0;j<(1 << d1);j++)
		{	
			if(f[d1][i][j])
				v1[i].push_back(j);
		}
	}
	for(re i=1;i<=n;i++)
	{
		for(re j=0;j <(1<<d2);j++)
		{	
			if(f2[d2][i][j])
				v2[i].push_back(j);
		}
	}
	for(re k=1;k<=n;k++)
	{
		for(re i=0;i<v1[k].size();i++)
		{
			for(re j=0;j<v2[k].size();j++)
			{
				if(vis[v1[k][i]<<(d2)|v2[k][j]])
					continue;
				vis[v1[k][i]<<(d2)|v2[k][j]]=1;
				++ans;
			}
		}
	}			
	printf("%lld\n",ans);
	return 0;
}


T3 z

留坑