20240218比赛总结

T1 家庭作业

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

对所给出的A,B的因数分解质因数,对于每个质因子,取最小的次数,然后相乘即可。

#include<cstdio>
#define ll long long
using namespace std;
int n,m,a,b,cnt[50005],p[50005],p1[1005],mod=1000000000,idx,cnt2[50005];
bool vis[50005];
ll ans=1;
void get_prime()
{
	for(int i=2;i<=50000;i++)
	{
		if(!vis[i]) p[++idx]=i;
		for(int j=1;i*p[j]<=n;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
}
int main()
{
	get_prime();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		for(int j=1;j<=idx;j++)
		{
			while(a%p[j]==0)
			{
				a/=p[j];
				cnt[j]++;
			}
		}
		if(a>1) p1[i]=a;
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&b);
		for(int j=1;j<=idx;j++)
		{
			while(b%p[j]==0)
			{
				b/=p[j];
				if(cnt2[j]<cnt[j])
				{
					cnt2[j]++;
					ans=ans*p[j]%mod;
				}
			}
		}
		if(b>1)
		{
			for(int j=1;j<=n;j++)
			{
				if(b==p1[j])
				{
					ans=ans*p1[j]%mod;
					p1[j]=0;
					break;
				}
			}
		}
	}
	printf("%lld",ans%mod);
	return 0;
}

T2 距离之和

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

在平面直角坐标系上很难看出来每个关键点与动点的曼哈顿距离的变化规律,但因为曼哈顿距离计算方法中,x,y坐标互不影响,所以将x,y分开考虑,以操作I为例,很容易发现,移动前x坐标大于当前位置a的会距离减1,其余距离加1,以此类推,就可得到答案

注意,不要用一些奇怪的东西,不然连暴力分也保不住!

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string>
#include<iostream>
#define ll long long
using namespace std;
int n,m,x[100005],y[100005],ax,ay,bx,by,a,b;
ll ans;
string s;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		ans=ans+abs(x[i])+abs(y[i]);
	}
	sort(x+1,x+n+1);
	sort(y+1,y+n+1);
	ax=upper_bound(x+1,x+n+1,a)-x-1;
	ay=upper_bound(y+1,y+n+1,b)-y-1;
	bx=lower_bound(x+1,x+n+1,a)-x-1;
	by=lower_bound(y+1,y+n+1,b)-y-1;
	cin>>s;
	for(int i=0;i<m;i++)
	{
		if(s[i]=='S')
		{
			ans=ans+2*ay-n;
			b++;
			ay=upper_bound(y+1,y+n+1,b)-y-1;
			by=lower_bound(y+1,y+n+1,b)-y-1;
		}
		if(s[i]=='J')
		{
			ans=ans-2*by+n;
			b--;
			ay=upper_bound(y+1,y+n+1,b)-y-1;
			by=lower_bound(y+1,y+n+1,b)-y-1;
		}
		if(s[i]=='I')
		{
			ans=ans+2*ax-n;
			a++;
			ax=upper_bound(x+1,x+n+1,a)-x-1;
			bx=lower_bound(x+1,x+n+1,a)-x-1;
		}
		if(s[i]=='Z')
		{
			ans=ans-2*bx+n;
			a--;
			ax=upper_bound(x+1,x+n+1,a)-x-1;
			bx=lower_bound(x+1,x+n+1,a)-x-1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T3 country

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

涉及字符串匹配,考虑hash或KMP(我只会hash),经计算得,最长的串的长度可达 1052 所以不能直接展开。

因为定义是无环的,所以考虑搜索(拓扑排序也行),对于每个串的引用,dfs求解,可以记忆化。

接着解决长度问题。

对于所包含的串,可以直接相加,对于交接部分,可以储存长度为min(len-1,l[i])的前后缀,然后在交接部分统计匹配数。

注意取模!千万不要写类似于pre[x][++id]=pre[t][j];的语句!!!

考后改的:

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#define ull unsigned long long
using namespace std;
int n,len,cnt[35],l[30];
string a,key,s[30],pre[30],lst[30];
ull hk[110],p=131,p1;
bool vis[30];
void dfs(int x)
{
	if(vis[x]) return;
	l[x]-=2;
	int id=0,tmp=l[x]+1;
	ull hs[10005];
	for(int i=0;i<=10000;i++) hs[i]=0;
	vis[x]=1;
//	printf("%d\n",x);
	for(int i=2;i<=tmp;i++)
	{
	//	cout<<s[x][i]<<" ";
		if(s[x][i]>='A'&&s[x][i]<='Z')
		{
			int t=s[x][i]-'A'+1;
			dfs(t);
			for(int j=1;j<=min(len-1,l[t]);j++)
			{
                id++;
				hs[id]=hs[id-1]*p+pre[t][j]-'a';
			}
			l[x]=min(1000,l[x]+l[t]-1);
			cnt[x]+=cnt[t];
			cnt[x]%=10000;
			if(l[t]>=len)
			{
				for(int j=len;j<=id;j++)
				{
					if(hs[j]-p1*hs[j-len]==hk[len]) cnt[x]=(cnt[x]+1)%10000;
				}
				id=0;
				for(int j=1;j<=min(len-1,l[t]);j++)
				{
                    id++;
					hs[id]=hs[id-1]*p+lst[t][j]-'a';
				}
			}
		}
		else
		{
            ++id;
			hs[id]=hs[id-1]*p+s[x][i]-'a';
		}
	}
	for(int j=len;j<=id;j++)
	{
		if(hs[j]-p1*hs[j-len]==hk[len]) cnt[x]=(cnt[x]+1)%10000;
	}
	if(l[x]<len)
	{
		id=0;
		for(int i=2;i<=tmp;i++)
		{
			if(s[x][i]>='A'&&s[x][i]<='Z')
			{
				int t=s[x][i]-'A'+1;
				for(int j=1;j<=min(len-1,l[t]);j++)
				{
                    id++;
					pre[x][id]=pre[t][j];
				}
			}
			else
			{
                id++;
				pre[x][id]=s[x][i];
			}
		}
		lst[x]=pre[x];
	}
	else
	{
		id=0;
		for(int i=2;i<=tmp;i++)
		{
			if(id==len-1) break;
			if(s[x][i]>='A'&&s[x][i]<='Z')
			{
				int t=s[x][i]-'A'+1;
				for(int j=1;j<=min(len-1,l[t]);j++)
				{
                    id++;
					pre[x][id]=pre[t][j];
					if(id==len-1) break;
				}
			}
			else
			{
                id++;
				pre[x][id]=s[x][i];
			}
		}
		id=0;
		for(int i=tmp;i>1;i--)
		{
			if(id==len-1) break;
			if(s[x][i]>='A'&&s[x][i]<='Z')
			{
				int t=s[x][i]-'A'+1;
				for(int j=min(len-1,l[t]);j>0;j--)
				{
                    id++;
					lst[x][id]=lst[t][j];
					if(id==len-1) break;
				}
			}
			else
			{
                id++;
				lst[x][id]=s[x][i];
			}
		}
		for(int i=1;i<=len/2;i++) swap(lst[x][i],lst[x][len-i]);
	}
}
int main()
{
	scanf("%d",&n);
	cin>>a;
	a="  "+a;
	for(int i=1;i<=n;i++)
	{
		cin>>s[i];
		l[i]=s[i].size();
	}
	cin>>key;
	len=key.size();
	key=" "+key;
	p1=1;
	for(int i=1;i<=len;i++)
	{
		hk[i]=hk[i-1]*p+key[i]-'a';
		p1*=p;
	}
//	printf("%d\n",len);
	s[n+1]=a;
	l[n+1]=s[n+1].size();
	for(int i=1;i<=n+1;i++) lst[i]=key,pre[i]=key; 
	dfs(n+1);
	printf("%d",cnt[n+1]%10000);
	return 0;
}
/*

*/

T4 太空飞船

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

看到数据范围,易考虑到排列组合,很明显,如果正向计算,式子会很复杂,DFS会T,考虑逆向计算,用总方案数减去不符合条件的方案数,因为有相同因数的数不能同时选4个,但类似于cnt[2]和cnt[3]在cnt[6]会重复计算,所以由容斥原理可得:

ans=Cn4+i=2nf[i]×Ccnt[i]4×(1)num[i]

其中,cnt[i]表示含有因数i的数的个数,num[i]是i的因数个数,f[i]为1的条件是i的质因子的次数均为1

f[i]和num[i]可以通过线性筛求,注意f[i]是1才转移,不然30,开long long,不然50

考后改的:

#include<cstdio>
#define ll long long
using namespace std;
int n,p[10000],m,num[10005],vis[10005],cnt[10005];
ll ans,x[10005];
void get_prime()
{
	for(int i=2;i<=10000;i++)
	{
		if(!vis[i])
		{
			p[++m]=i;
			num[i]=1;
		}
		for(int j=1;i*p[j]<=10000;j++)
		{
			if(i%p[j]&&vis[i]!=2)
			{
				vis[i*p[j]]=1;
				num[i*p[j]]=num[i]+1;
			}
			else
			{
				vis[i*p[j]]=2;
			}
			if(i%p[j]==0) break;
		}
	}
}
int main()
{
	//freopen("1.txt","r",stdin);
	get_prime();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		x[i]=1ll*i*(i-1)*(i-2)*(i-3)/24;
	}
	if(n<4)
	{
		printf("0");
		return 0;
	}
	ans=1ll*x[n];
	for(int i=1;i<=n;i++)
	{
		int a;
		scanf("%d",&a);
		for(int j=1;j*j<=a;j++)
		{
			if(a%j==0)
			{
				cnt[j]++;
				if(a/j!=j) cnt[a/j]++;
			}
		}
	}
	for(int i=2;i<=10000;i++)
	{
		if(vis[i]==2||cnt[i]<4) continue;
		//printf("%d %d\n",i,cnt[i]);
		if(num[i]%2) ans=ans-x[cnt[i]];
		else ans=ans+x[cnt[i]];
	}
	printf("%lld",ans);
	return 0;
}
posted @   wangsiqi2010916  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示