[2025五一lyyz集训] 数论+练习赛
一如既往地在 \(lyyz\) 集训。
5.1
今天是练习赛,但感觉今天不适合打比赛,交几道挂几道,被教练一顿凶。
练习赛结果:\(100+26+62+22\)
其中有道 \(DP\) 不错:
C密码本解密:
大概题意就是:
给你一个字符串 \(s\),然后给你 \(n\) 个密码字符串,需要用这些密码字符串拼出 \(s\),字符串拼接过程中可以调换字符顺序。
定义变形成本为原词与变形词不同字符位置的数量。
正解:
\(DP\) 一下,由于 \(n\),\(|s|\) 很小,可以首先枚举一下位置 \(i\) ,然后枚举密码字符串 \(j\),\(dp[i][j]\) 表示以 \(i\) 为开头,接入第 \(j\) 个密码字符串的最小成本,然后 \(dp\) 就好了
Code
#include<bits/stdc++.h>
using namespace std;
string s;
int n;
int tot[103];
string t[2004];
int dp[104];
int cost(string a,string b){
string A=a,B=b;
sort(A.begin(),A.end());
sort(B.begin(),B.end());
if(A!=B) return -1;
int sum=0;
for(int j=0;j<=b.size();j++){
if(a[j]!=b[j]) sum++;
}
return sum;
}
signed main(){
cin>>s>>n;
for(int i=1;i<=n;i++){
cin>>t[i];
}
memset(dp,0x3f3f3f,sizeof dp);
dp[0]=0;
for(int i=0;i<s.size();i++){
if(dp[i]==0x3f3f3f||dp[i]==1061109567) continue;
for(int j=1;j+i-1<s.size();j++){
for(int p=1;p<=n;p++){
int c=cost(s.substr(i,j),t[p]);
if(c==-1) continue;
else{
dp[i+j]=min(dp[i+j],dp[i]+c);
}
}
}
}
if(dp[s.size()]!=1061109567) cout<<dp[s.size()];
else cout<<"-1";
}
/*
ogodtsneeencs
6
go
good
do
sentences
tense
scen
*/
5.2
今天主要讲了数论。
A.Luogu P3927
这道题求 \(n!\) 在转 \(k\) 进制后末尾最多有几个 \(0\)
首先,转 \(k\) 进制是不断除以 \(k\),那么如何让末尾为 \(0\) 呢?
肯定就是整除 \(k\)。
那么这道题就成了 \(n!\) 可以整除几个 \(k\)
由于\(n,k\) 很大,于是我们考虑质因数分解。
那么 \(0\) 的个数也就是 \(n!\) 的每个质因子次数除以 \(k\) 的每个质因子质数的最小值。
那么我们想如何求出 \(n!\) 的每个质因子次数呢。
我们可以先枚举 \(k\) 的质因子(因为这个简单的多),然后只需要求这个质数在 \(n!\) 里的次数就行了。
我们知道 \(n!=1 \times 2 \times 3 \times ... \times n\)
那么一个质数 \(p\) 在里面是不是每 \(p\) 个数出现一个 \(k \times p\),那么当第 \(p\) 个 \(p\) 的倍数出现时,这个数是 \(p \times p\) ,将这些 \(p\) 的倍数提出来是不是成了:
其中括号里面的又回到了一开始的样子,那么还是每 \(p\) 个出现一次 \(p\) ,所以只要不断以此类推就行了。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k;
inline int __(int x,int b){
int sum=0;
while(x){
sum+=x/b;
x/=b;
}
return sum;
}
signed main(){
cin>>n>>k;
int ans=7e18;
for(int i=2;i*i<=k;i++){
int sum=0;
while(k%i==0){
sum++;
k/=i;
}
if(sum!=0) ans=min(ans,__(n,i)/sum);
}
if(k!=0&&k!=1){
ans=min(ans,__(n,k));
}
cout<<ans;
}
B.codeforces 1295D
简单题意:
给定两个整数 \(a\) 和 \(m\)。计算满足 \(0<x<m\) 且 \(gcd(a,m)= gcd(a +x,m)\) 的整数的个数。
首先:由于 \(a,m\) 已经给出,所以 \(gcd(a,m)\) 是个常数 ,我们设 \(gcd(a,m)=g\) ,则式子转化为:
式子两边同时除以 \(g\):
我们知道:
所以我们可以把 \(\div g\) 看做 \(\cdot \frac{1}{g}\),得:
由于,\(m,g\) 都是常数,所以 \(\frac{m}{g}\) 也为常数,设为 \(d\),得:
由于 \(a\)、\(g\) 为常数,所以当每个 \(x\) 都有唯一一个 \(\frac{a+x}{g}\) 为之对应。由于 \(\frac{a}{g} < \frac{a+x}{g} < \frac{m+a}{g}\),,所以把问题转化为:在 \(\frac{a}{g}\) 到 \(\frac{m+a}{g}\),有多少个数与 \(d\) 互质。
发现 \([\frac{a}{g},\frac{m+a}{g}]\) ,其实是由 \([0,m]\) 平移一段得到的,由于质数的出现是有周期性的,所以就成了求 \(\varphi(d)\)
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T;
int a,m;
int gcd(int a,int b){
if(b==0) return a;
else{
return gcd(b,a%b);
}
}
int oula(int x){
int ans=x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
ans=ans/i*(i-1);
while(x%i==0){
x/=i;
}
}
}
if(x>1) ans=ans/x*(x-1);
return ans;
}
signed main(){
cin>>T;
while(T--){
cin>>a>>m;
int g=gcd(a,m);
int d=m/g;
cout<<oula(d)<<endl;
}
}
C.消失的\(LCM\)
简要题意: 给一个正整数 \(N\),找一个比 \(N\) 大的正整数 \(M\),定义数值\(A=lcm(N+ 1,N + 2,…, M)\) ,数值 \(B=lcm(1,2,…, M)\),使得 \(A=B\) 。
一看到这道题,我们发现,\(A,B\) 什么关系?
明显的包含关系。
那么我们把 \(B\) 分为两段,前部分:\(C=lcm(1,2,...,N)\),后部分很明显就是 \(A\) 了,那么 \(B=lcm(C,A)\)
那么让 \(B=A\) 就是
这就是一道小学题了:问 \(C,A\) 的关系。
肯定是 \(A=kC\)
倍数关系的话,是不是分解质因数后,\(A\) 的每个质因子的次数都要大于 \(C\) 的质因子次数。
那我们枚举质因子,取最大值就好了。
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e6;
int prime[MAXN+10], num;
bool a[MAXN+10];
void init(){
num = 0;
a[1] = false;
for(int i = 2; i <= MAXN; i++) a[i] = true;
for(int i = 2; i <= MAXN; i++){
if(a[i]){
prime[++num] = i;
}
for(int j = 1; j <= num; j++){
if(i*prime[j] > MAXN) break;
a[i*prime[j]] = false;
if(i%prime[j] == 0) break;
}
}
}
int slove(int n){
int res = 2;
for(int i = 1; prime[i] <= n && i <= num; i++){
int sum = prime[i];
while(sum <= n/prime[i]) sum *= prime[i];
res= max(res, (n/sum + 1)*sum);
}
return res;
}
int main(){
int n;
init();
scanf("%d", &n);
printf("%d\n", slove(n));
return 0;
}
5.3
依旧模拟赛。
预期分数:[100+100+100+100+0+0+0+0]
实际分数:[100+50+100+50+0+0+0+0]
第二题是被语文阅读理解击败了/ll
C.\(p\)平方\(q\)
简要题意:给一个 \(N\)( \(N \leq 9 \times 10^{18}\) ) ,保证 \(N=p^{2} \cdot q\) (\(p\)、\(q\)为质数),求 \(p\)、\(q\)
首先,由于 \(p\)、\(q\)为质数 ,根据唯一分解定理,我们如果对 \(N\) 进行质因数分解,肯定能求出来,但是时间复杂度 \(\sqrt{N}\) ,是不能接受的。
由于只有 \(p*p*q\),所以我们只要知道 \(p\) 或 \(q\) 其中一个就能知道另一个。很容易就能发现 \(p\) 或 \(q\) 是小于等于 $ \sqrt[3]{N}$ ,这就可以接受了。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T;
long long a;
signed main(){
cin>>T;
while(T--){
cin>>a;
int s=0;
int ans2=0;
int ans1=0;
for(int i=2;i*i*i<=a;i++){
int sum=0;
if(a%i==0){
if((long long)(a/i)%i==0){
cout<<i<<" "<<a/i/i<<endl;
break;
}
else{
int p=sqrt(a/i);
cout<<p<<" "<<i<<endl;
break;
}
}
}
}
}
/*
3
2023
63
1059872604593911
*/
五一集训这就结束了,祝大家五一快乐!