20240314比赛总结

T1 [USACO09MAR] Cow Frisbee Team S

https://gxyzoj.com/d/hzoj/p/3652

dpi,j表示目前是第i头奶牛,余数为j的方案数,则转移方程为:

dpi,j=dpi1,j+dpi1,jwi

最后-1即可,注意模数!!!

代码:

#include<cstdio>
using namespace std;
const int p=1e8;
int n,f,dp[2005][1005],a[2005];
int main()
{
	scanf("%d%d",&n,&f);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i]%=f;
	}
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<f;j++)
		{
			dp[i][j]=(dp[i-1][j]+dp[i-1][(j-a[i]+f)%f])%p;
		}
	}
	printf("%d",(dp[n][0]-1+p)%p);
	return 0;
}

T2 Prufer 序列多源生成树

https://gxyzoj.com/d/hzoj/p/3653

一个奇怪的方法,第一遍DFS记录子树内经过自己的最长链(dp1[i])与次长链(dp2[i]),但最长链与次长链不能出自同一个儿子节点

第二遍DFS,计算子树外经过自己的最长链(f[i]):

  1. 直接从父亲转移 fi=ffa+1
  2. fa子树内的最长链不经过u fi=dp1fa+1
  3. fa子树内的最长链不过u fi=dp2fa+1

最后取max即可,函数要么用void,要么有返回值(100->0)!!!

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,head[100005],edgenum;
struct edge{
	int to,nxt;
}e[200005];
void add_edge(int u,int v)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
int dp1[100005],dp2[100005],f[100005],val=1e9,ans=1e9;
void dfs(int u,int fa)
{
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		if(dp1[v]+1>=dp1[u])
		{
			dp2[u]=dp1[u];
			dp1[u]=dp1[v]+1;
		}
		else if(dp1[v]+1>dp2[u])
		{
			dp2[u]=dp1[v]+1;
		}
	}
}
void dfs2(int u,int fa)
{
	if(dp1[fa]==dp1[u]+1) f[u]=dp2[fa]+1;
	else f[u]=dp1[fa]+1;
	f[u]=max(f[u],f[fa]+1);
	if(max(f[u],dp1[u])<val) val=max(f[u],dp1[u]),ans=u;
	else if(val==max(f[u],dp1[u])&&ans>u) ans=u;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa) continue;
		dfs2(v,u);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int v,u;
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs(1,0);
	f[0]=dp1[0]=dp2[0]=-1;
	dfs2(1,0);
	printf("%d",ans);
	return 0;
}

T3 [HNOI2011] 数学作业

https://gxyzoj.com/d/hzoj/p/3654

容易想到30分做法,设dpi表示接了i个数时的余数,有:

dpi=(dpi1×10log10i+i)modm

显然会T,考虑矩阵快速幂

式子:

[dpii+11]=[10t00110011]×[dpi1i1]

代码:

#include<cstdio>
#include<cstring>
#define ull unsigned long long
#define ll long long
using namespace std;
int m;
ull n;
struct mat{
	ull a[5][5];
	void clear()
	{
		memset(a,0,sizeof(a));
	}
	mat operator *(const mat &x)
	{
		mat ans;
		ans.clear();
		for(int i=1;i<=3;i++)
		{
			for(int j=1;j<=3;j++)
			{
				for(int k=1;k<=3;k++)
				{
					ans.a[i][j]+=a[i][k]*x.a[k][j];
					ans.a[i][j]%=m;
				}
			}
		}
		return ans;
	}
};
mat qpow(mat res,mat x,ull y)
{
	while(y)
	{
		if(y&1) res=res*x;
		x=x*x;
		y>>=1;
	}
	return res;
}
mat x,y;
int main()
{
	scanf("%llu%d",&n,&m);
	ull j=1;
	x.a[1][2]=x.a[1][3]=1;
	y.a[2][1]=y.a[2][2]=y.a[3][2]=y.a[3][3]=1;
	while(1)
	{
		j=j*10;
		y.a[1][1]=j%m;
		if(n<j)
		{
			x=qpow(x,y,n-(j/10-1));
			break;
		}
		else
		{
			x=qpow(x,y,j-j/10);
		}
	}
	printf("%llu",x.a[1][1]);
	return 0;
} 

T4 [Jxoi2012]奇怪的道路

https://gxyzoj.com/d/hzoj/p/461

第一次考场上推出来紫题qwq

又是一个奇怪的方法

可以发现,当将某一些成对的边去掉后,依然满足条件,所以当其他满足后,用隔板法组合数求解即可

所以设dpi,j,s表示目前是第i个点,用了j条边,i+1~i+k号点所连边数的奇偶状态

转移方程:

dpi+1,j+b[st],(s>>1) xor st+=dpi,j,s

代码:

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int p=1000000007;
int n,m,k,b[305];
ll dp[35][305][35],fac[1005],inv[1005];
int lowbit(int x)
{
	return x&(-x);
}
int get(int x)
{
	int tmp=0;
	while(x)
	{
		tmp++;
		x-=lowbit(x);
	}
	return tmp;
}
ll qpow(ll x,int y)
{
	ll res=1;
	while(y)
	{
		if(y&1) res=res*x%p;
		x=x*x%p;
		y>>=1;
	}
	return res;
}
ll C(int n,int m)
{
	return fac[n]*inv[m]%p*inv[n-m]%p;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	fac[0]=1;
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			if(j-i<=k) sum++;
		}
	}
	for(int i=1;i<=sum+m;i++)
	{
		fac[i]=fac[i-1]*i%p;
	}
	inv[sum]=qpow(fac[sum],p-2);
	for(int i=sum-1;i>=0;i--)
	{
		inv[i]=inv[i+1]*(i+1)%p;
	}
	for(int i=1;i<(1<<k);i++)
	{
		b[i]=get(i);
	}
	dp[0][0][0]=1;
	for(int i=0;i<n;i++)
	{
		for(int s=0;s<(1<<k);s++)
		{
			for(int j=0;j<=m;j++)
			{
				if(dp[i][s][j]==0) continue;
				int t=(s&1);
				for(int st=0;st<(1<<min(n-i-1,k));st++)
				{
					if(j+b[st]>m||(t!=b[st]%2&&i!=0)) continue;
					dp[i+1][(s>>1)^st][j+b[st]]+=dp[i][s][j];
					dp[i+1][(s>>1)^st][j+b[st]]%=p;
				}
			}
		}
	}
	ll ans=0;
	for(int i=0;i<=m;i++)
	{
		if((m-i)%2) continue;
		if(i!=m)
		ans=(ans+dp[n-1][0][i]*C((m-i)/2+sum-1,sum-1)%p)%p;
		else ans=(ans+dp[n-1][0][i])%p;
//		printf("%d %d\n",dp[n-1][0][i],C(2*sum-1,(m-i)/2-1));
	}
	printf("%lld",ans%p);
	return 0;
}
posted @   wangsiqi2010916  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示