8-26模拟赛解题报告

今天勉强打了打暴力,没有爆零。。(同机房还有一个(170)的dalao┌(。Д。)┐


T1.chess

题目大意:一个棋盘上有一些格子有黑棋,有一些格子有白棋

考场上这题差一点想到正解,可是一时脑抽,就在一条路上走不回来了。。

直接讲正解吧,这题应该属于插头DP,也叫轮廓线DP,从左上延轮廓线走到右下,向下走为(1),向右走为(0),然后状态就有了,强行(O(n^2))暴力枚举两个点转移,然后判断单个点,就AC了。时间复杂度为(O(C_{2n}^nn^2))

小结:

解题关键:想到插头DP的经典状态

芝士点:插头DP

duliu出题人,竟然卡常!!!

手起,码落:

#include<bits/stdc++.h>
#define re register
using namespace std;
int n,f[(1<<24)+10],len,val[13][13],a[13][13],num[(1<<24)+1],num1[30],mid;
char ch[13][13];
void work(int x)
{
	num1[0]=(x&1);
	for(re int j=1;j<(n<<1);j++)
	{
		num1[j]=num1[j-1]+((x>>j)&1);
		if((x>>j&1)==0||(x>>j-1&1))continue;
		f[x]=min(f[x],f[x^(1<<j)^(1<<j-1)]+val[num1[j]][j-num1[j]+1]);
		for(re int k=1;k<j;k++)
		{
			if((x>>k&1)&&(x>>k-1&1)==0&&a[num1[j]][j-num1[j]+1]+a[num1[k]][k-num1[k]+1]==61)
				f[x]=min(f[x],f[x^(1<<j)^(1<<j-1)^(1<<k)^(1<<k-1)]+abs(val[num1[j]][j-num1[j]+1]-val[num1[k]][k-num1[k]+1]));
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(re int i=1;i<=n;i++)
	{
		scanf("%s",ch[i]+1);
		for(re int j=1;j<=n;j++)
			a[i][j]=ch[i][j]-46;
	}
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=n;j++)
			scanf("%d",&val[i][j]);
	memset(f,63,sizeof(f)),len=((1<<(n<<1))-1)^((1<<n)-1),f[(1<<n)-1]=0;
	for(re int i=1;i<=len;i++)
	{
		num[i]=num[i>>1]+(i&1);
		if(num[i]==n)work(i);
	}
	return printf("%d",f[len]),0;
}

T2.tree

题目大意:给定(n)个点,第(i)个点的限制为度数不能超过(a_i)。现在对于每一个(1le sle n),问从这(n)个点中选出(s)个点组成有标号无根树的方案数对(10^9+7)取模后的结果。

一上午死磕T1,第二题看都没看,没想到竟然是一个模板题!(虽然我不会

前置芝士:Prufer序列

这题需要一个神奇的东西——Prufer序列,它有一个很好的性质就是和有标号无根树一一对应,主要生成过程如下:

  • 选择一个序号最小的叶子结点,把和这个点相连的点加入序列末端,然后删除这个叶子结点及这条边。

  • 重复以上操作直至剩余两个节点

而Prufer序列生成无根树的过程也很简单,大家可以自己思考思考 (就是我自己不愿意打了

进入正题:

这题求的是有标号无根树的方案树,其实也就是Prufer序列的方案数,直接DP就好了。(f[i][j][k])表示前(i)个节点选择(j)个,在Prefer序列中填了(k)个数的方案数除以((j-2)!)

然后状态转移方程就出来了:

[f[i+1][j][k]+=f[i][j][k] ]

[f[i+1][j+1][k+c]+=frac{f[i][j][k]}{c!} ]

其中(c)为枚举第(i)个节点的度数限制的值且(0le cle a_{i+1}-1)

所以最后的答案就是(f[n][s][s-2] imes (s-2)!)

小结:

解题关键:这题算模板题吧,想到模板就好了

芝士点:Prefer序列,有标号无根树计数

手起,码落:

#include<bits/stdc++.h>
#define re register
#define mod 1000000007
using namespace std;
const int N=105;
inline long long Pow(long long x,int y)
{
	re long long sum=1;
	for(;y;x=(x*x)%mod,y>>=1)
		if(y&1)sum=(sum*x)%mod;
	return sum;
}
int n,a[N];
long long f[N][N][N],p[N]={1},inv[N];
int main()
{
	scanf("%d",&n);
	if(n==0)return printf("0"),0;
	for(re int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(re int i=1;i<=n;i++)p[i]=p[i-1]*i%mod;
	inv[n]=Pow(p[n],mod-2);
	for(re int i=n-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
	f[0][0][0]=1;
	for(re int i=0;i<=n;i++)
		for(re int j=0;j<=i;j++)
			for(re int k=0;k<=n-2;k++)
			{
				if(f[i][j][k]==0)continue;
				(f[i+1][j][k]+=f[i][j][k])%=mod;
				for(re int l=0;l<a[i+1]&&k+l<=n-2;l++)
					(f[i+1][j+1][k+l]+=f[i][j][k]*inv[l]%mod)%=mod;
			}
	printf("%d ",n);
	for(re int i=2;i<=n;i++)printf("%lld ",f[n][i][i-2]*p[i-2]%mod);
	return 0;
}

T3.divide

咕咕咕~~

原文地址:https://www.cnblogs.com/jkzcr01-QAQ/p/13574150.html