2023.7.5测试

T1 排队打水

小学级别的贪心题

T2 Oversleeping

一道不错的扩欧题,一开始没反应过来,一直搞不等式……

根据题意可以知道在 B 站停留的时间区间为 [k(2n+2m)+n,k(2n+2m)+n+m)(kZ)

清醒的时间区间为 [k(p+q)+p,k(p+q)+p+q)(kZ)

由于 m,q 的范围小,所以我们可以枚举 i[0,m),j[0,q),那么接下来我们只需令 k(2n+2m)+n+i=k(p+q)+p+j,移项一下得:

(2n+2m)k(p+q)k=p+jni

扩欧求解即可

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int T;
LL p,q,n,m;

LL exgcd(LL a,LL b,LL &x,LL &y)
{
	if(b==0)
	{
		x=1;  y=0;
		return a;
	}
	
	LL d=exgcd(b,a%b,x,y);
	LL tmp=x;
	x=y;  y=tmp-(a/b)*y;
	return d;
	 
}

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld%lld%lld",&n,&m,&p,&q);
		
		LL a,b,c,d,x,y,ans=(2*n+2*m)*(p+q)+1;
		a=2*n+2*m;
		b=p+q;
		for(int i=0; i<m; i++)
		{
			for(int j=0; j<q; j++)
			{
				c=p-n+j-i;
				d=exgcd(a,b,x,y);
				if(c%d)
					continue;
				
				x=x*c/d;
				x=(x%(b/d)+(b/d))%(b/d);
				ans=min(ans,x*(2*n+2*m)+n+i);
			}
		}
		
		if(ans==(2*n+2*m)*(p+q)+1)
			printf("infinity\n");
		else
			printf("%lld\n",ans);
	}

	return 0;
}

T3 XOR Tree

一道神仙题,膜拜zby大佬赛时AC

学会了:对边权下手一次 O(n),不好弄,所以把边权转移成点权

定义一个点的点权为与它相连的所有边的边权异或和,然后我们发现对 (x,y) 这条链上的修改其实只对端点 x,y 造成影响。而当最终边权为 0,也就意味着点权为 0

因此,问题转化成每次任意选 2 个点进行异或操作,求最终每个点点权为 0 的最小步数

显然可能有些点权是相同的,那我们贪心地优先将它们异或为 0,直到每种数字出现的次数至多为 1

又因为值域最大 15,所以考虑状压。设 f[s] 表示集合 s 异或成 0 的最小步数。而我们注意到,如果 s 集合里的数异或和都不为 0,那不管怎么样都无法达成目的,因为一次操作对总异或和没有影响。又注意到若对于一个合法的集合 s 最暴力的方法就是两两配对,所以初始化 f[s]=|s|1

那么我们枚举集合的每个子集进行转移即可,注意最终答案要加上贪心的操作

#include<bits/stdc++.h>
using namespace std;

const int N=100010,M=18;

int n,a[N],ans;
int s,cnt[M],t[1<<M],f[1<<M],sum[1<<M];

int main()
{
	memset(f,0x3f,sizeof(f));
	
	scanf("%d",&n);
	for(int i=1; i<n; i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		a[++x]^=z;  a[++y]^=z;
	}
	
	for(int i=1; i<=n; i++)
		cnt[a[i]]++;
	
	for(int i=1; i<=15; i++)
	{
		ans+=cnt[i]/2; //贪心
		s+=((cnt[i]&1)<<i-1); //记录最终状态s
	}
	
	for(int i=1; i<=(1<<15)-1; i++)
		t[i]=t[i>>1]+(i&1); //预处理每个状态几个1

	for(int i=1; i<=(1<<15)-1; i++)
	{
		for(int j=1; j<=15; j++) //预处理每个状态的异或和
			if(i&(1<<j-1))
				sum[i]^=j;
		if(!sum[i])
			f[i]=t[i]-1;
	}
		
	f[0]=0;			
	for(int i=0; i<=(1<<15)-1; i++)
	{
		if(sum[i]!=0)
			continue;
		for(int j=i; j; j=(j-1)&i) //枚举子集
			f[i]=min(f[i],f[j]+f[i^j]);
	}
	
	printf("%d",ans+f[s]);
	
	return 0;
}

T4 棋子

一个 nm 列的棋盘,棋子可以攻击同一行内且距离不超过 d 的棋子。现在可以在棋盘放任意多个棋子,但任意两个棋子都不能相互攻击。问有多少种不同的方案,答案模 100003

1n,m,d109

一道需要分段来做的题

显然行和行之间无影响,只需计算一行的方案数 ans,答案即为 ansn1

在考场上主要往组合数方面想,暴力枚举一行放了 i 个棋子,则可以理解成有 i1 个空中间至少有 d 个格子,将这些格子去掉在剩下的格子就可以随便选 i 个,所以贡献为

(m(i1)×di)

当时没管数据范围直接交上去拿了 40

注意到模数比较小,其实可以套用 Lucas 定理,把复杂度优化到 O(mdlog100003m)。当然,这样做的前提是 d 比较大,如果 d 很小还这样做显然超时

d 比较小时怎么办呢?

考虑 dp,设 f[i] 表示前 i 个格子放置棋子的方案数,则因为第 i 个格子可放可不放,所以 f[i]=f[i1]+f[id1](i>d)。当 0id 时,f[i]=i+1

这显然可以通过矩阵快速幂来进行优化。下面以 d=3 为例:

构造初始矩阵:

[f[3]f[2]f[1]f[0]]

构造转移矩阵:

[1000010000101001]

设置分段点为 d=200,矩阵快速幂的复杂度约为 O(log2200×2003)

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N=10000010,M=210;
const int MOD=100003;

int n,m,d;
int fac[N],inv[N];

struct matrix
{
	int row,col,data[M][M];
	matrix(int r,int c){row=r;col=c;memset(data,0,sizeof(data));}
};

matrix operator * (const matrix &x,const matrix &y)
{
	matrix c(x.row,y.col);
	for(int i=1; i<=x.row; i++)
		for(int j=1; j<=y.col; j++)
			for(int k=1; k<=x.col; k++)
				(c.data[i][j]+=1LL*x.data[i][k]*y.data[k][j]%MOD)%=MOD;
	return c;
}

int ksm(int x,int y)
{
	int res=1;
	while(y)
	{
		if(y&1)
			res=1LL*res*x%MOD;
		x=1LL*x*x%MOD;
		y>>=1;
	}
	return res;
}

void prework()
{
	fac[0]=inv[0]=1;
	for(int i=1; i<MOD; i++)
	{
		fac[i]=1LL*fac[i-1]*i%MOD;
		inv[i]=ksm(fac[i],MOD-2);
	}
}

int C(int a,int b)
{
	if(b>a)
		return 0;
	return 1LL*fac[a]*inv[b]%MOD*inv[a-b]%MOD;	
}

int Lucas(int a,int b)
{
	if(b>a)
		return 0;
	if(!a || !b)
		return 1;
	return 1LL*C(a%MOD,b%MOD)*Lucas(a/MOD,b/MOD)%MOD;
}

int main()
{
	prework();
	scanf("%d%d%d",&d,&n,&m);
	
	if(d<=200)
	{
		if(m<=d)
		{
			printf("%d",(ksm(m+1,n)-1+MOD)%MOD);
			return 0;
		}
		matrix ans(1,d+1);
		matrix a(d+1,d+1);
		
		for(int i=1; i<=d+1; i++)
			ans.data[1][i]=d+1-i+1;
		a.data[1][1]=a.data[d+1][1]=1;
		for(int i=2; i<=d+1; i++)
			a.data[i-1][i]=1;
		
		m-=d;
		for(; m; m>>=1)
		{
			if(m&1)
				ans=ans*a;
			a=a*a;
		}
		
		printf("%d",(ksm(ans.data[1][1],n)-1+MOD)%MOD);
	} 
	else
	{
		int ans=0;
		for(int i=0; m-d*(i-1)>=i; i++)
			(ans+=Lucas(m-d*(i-1),i))%=MOD;
		
		printf("%d",(ksm(ans,n)-1+MOD)%MOD);
	}
	
	return 0;
}
posted @   xishanmeigao  阅读(8)  评论(0编辑  收藏  举报
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示