NOIP2024集训Day60

非常好数论复习。

CF986F Oppa Funcan Style Remastered

给定 nk,问是否能将 n 分为若干个 k 的因数(1 除外)之和,每个因数都可以被选多次。

n1018k1015,最多 50 种不同的 k

一共t组询问,t104


首先,注意到我们可以把选的数的范围缩小到 k 的所有质因子,这是不超过 20 个的。

那么现在问题就变成了判断能否用若干个数加出 n,容易发现这是同余最短路的模板。

但是还有一个问题就是同余最短路的复杂度是与最小的数相关的,如果 k 是一个大质数的话复杂度是无法接受的。

观察到如果当 k 有超过 3 个不同的质因子时,最小的质因子是小于 105 的,所以我们考虑对于质因子数超过 3 的 k 跑同余最短路,而对质因子数为 0,1,2 的 k 单独处理:

  • k 的质因子数为 0,即 k=1 时,无解。
  • k 的质因子数为 1,即 k=pa 时,判断 n 是否是 p 的质数。
  • k 的质因子数为 2 时,记这两个质因子分别为 a,b,那用裴蜀定理判断 ax+by=n 是否存在非负整数解即可。

#include<bits/stdc++.h>
#define int __int128
using namespace std;
char buf[1000005],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
int read(){
	int x=0,f=0,c=gc();
	while(!isdigit(c))f|=(c=='-'),c=gc();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=gc();
	return f?-x:x;
}
int exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1,y=0;
		return a;
	}
	int gcd=exgcd(b,a%b,x,y);
	int t=y;
	y=x-(a/b)*y;
	x=t;
	return gcd;
}
int T,n,k,tot;
map<int,int>M;
vector<int>ps[55];
int dis[55][100005],sum[55],p[55];
bool vis[100005];
struct node{
	int x,k;
	bool operator<(const node &t)const{return k>t.k;}
};
priority_queue<node>q;
void D(int id){
	for(int i=0;i<p[id];i++)dis[id][i]=1.5e18;
	memset(vis,0,sizeof(vis));
	dis[id][0]=0;
	q.push(node({0,0}));
	while(!q.empty()){
		node tmp=q.top();
		q.pop();
		if(vis[tmp.x])continue;
		vis[tmp.x]=1;
		for(int i=1;i<sum[id];i++){
			int v=(tmp.x+ps[id][i])%p[id],w=ps[id][i];
			if(dis[id][v]>dis[id][tmp.x]+w){
				dis[id][v]=dis[id][tmp.x]+w;
				q.push(node({v,dis[id][v]}));
			}
		}
	}
}
signed main()
{
	T=read();
	while(T--){
		n=read(),k=read();
		if(k==1){
			puts("NO");
			continue;
		}
		int id;
		if(!M.count(k)){
			id=M[k]=++tot;
			for(int i=2;i*i<=k;i++){
				if(k%i==0){
					ps[tot].push_back(i);
					while(k%i==0)k/=i;
				}
			}
			if(k>1)ps[tot].push_back(k);
			sum[tot]=ps[tot].size();
			p[tot]=ps[tot][0];
			if(sum[tot]>2){
				D(id);
			}
		}
		else id=M[k];
//		cerr<<id<<' '<<sum[id]<<endl;
		if(sum[id]==1)puts(n%p[id]==0?"YES":"NO");
		else if(sum[id]==2){
			int a=ps[id][0],b=ps[id][1],x,y;
			exgcd(a,b,x,y);
			x*=n;
			y*=n;
//			print(x),pc(' '),print(y),pc(10);
			int l=(-x+b-1)/b,r=y/a;
//			print(l),pc(' '),print(r),pc(10);
			puts(l<=r?"YES":"NO");
		}
		else{
			puts(n>=dis[id][n%p[id]]?"YES":"NO");
		}
	}
	return 0;
}

CF571E Geometric Progressions

给定 n 以及 n 个正整数对 ai,bi

iai,bi 确定了一个序列 {ai,aibi,aibi2,aibi3,}

询问最小的在 n 个序列中都有出现的数,或者判断不存在。

n100ai,bi109,答案对 109+7 取模。


我们先特判掉存在 bi=1 的情况。

然后考虑将所有的 ai,bi 分解为质数的幂的乘积的形式。可以发现总共只有不超过 2000 种质数。

我们记 m 为不同的质数的数量,xi,j,yi,j 分别为 ai,bi 分解后第 j 个质数的指数。

随后我们考虑依次将 (ai,bi)(ai+1,bi+1) 合并。即我们要找到一组 (ci,ci+1) 满足 ci,ci+1\N1jm,xi,j+yi,jci=xi+1,j+yi+1,jci+1。容易发现这是一个方程组。

高斯消元后如果发现方程组:

  • 无解则表明原问题无解。
  • 存在唯一解就判断它对应的数是否满足后续所有的数列
  • 如果解出来发现 ciX(modY),就让 (aibiX,biY) 代替 (aibi),(ai+1,bi+1) 继续与后面合并,注意特殊处理一下 X=0 时的情况。

#include<bits/stdc++.h>
#define int long long
using namespace std;
char buf[1000005],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
int read(){
	int x=0,f=0,c=gc();
	while(!isdigit(c))f|=(c=='-'),c=gc();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=gc();
	return f?-x:x;
}
char puf[1000005];
int ptot;
#define pc(x) (ptot==1000000?(fwrite(puf,1,1000000,stdout),ptot=0,puf[ptot++]=x):puf[ptot++]=x)
void print(int x){
	if(x<0){
		pc('-');
		print(-x);
		return;
	}
	if(x>9)print(x/10);
	pc(x%10+'0');
}
const int mod=1e9+7;
int exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1,y=0;
		return a;
	}
	int gcd=exgcd(b,a%b,x,y);
	int t=y;
	y=x-(a/b)*y;
	x=t;
	return gcd;
}
int n,tot,p[2005],a[105],b[105],x[105][2005],y[105][2005];
int aa[2005][5];
struct node{
	int x,y,mx,my;
};
int fpow(int a,int b){
	int res=1;
	while(b){
		if(b&1)res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
node G(){
	for(int i=1;i<=2;i++){
		int k=0;
		for(int j=i;j<=tot;j++){
			if(aa[j][i]!=0){
				k=j;
				break;
			}
		}
		if(!k)continue;
		swap(aa[i][1],aa[k][1]);
		swap(aa[i][2],aa[k][2]);
		swap(aa[i][3],aa[k][3]);
		for(int j=1;j<=tot;j++){
			if(j!=i){
				int gcd=__gcd(aa[i][i],aa[j][i]),x=aa[i][i]/gcd,y=aa[j][i]/gcd;
				for(int l=1;l<=3;l++){
					aa[j][l]*=x;
					aa[j][l]-=aa[i][l]*y;
				}
			}
		}
	}
	int X=-1e10,Y=-1e10;
	for(int i=tot;i>=1;i--){
		if(aa[i][1]==0&&aa[i][2]==0){
			if(aa[i][3]){
				puts("-1");
				exit(0);
			}
		}
		else if(aa[i][1]==0){
			if(aa[i][3]%aa[i][2]){
				puts("-1");
				exit(0);
			}
			int yy=aa[i][3]/aa[i][2];
			if(Y!=-1e10&&Y!=yy){
				puts("-1");
				exit(0);
			}
			Y=yy;
		}
		else if(aa[i][2]==0){
			if(aa[i][3]%aa[i][1]){
				puts("-1");
				exit(0);
			}
			int xx=aa[i][3]/aa[i][1];
			if(X!=-1e10&&X!=xx){
				puts("-1");
				exit(0);
			}
			X=xx;
		}
		else{
			int xx=0,yy=0;
			int gcd=exgcd(aa[i][1],aa[i][2],xx,yy);
			if(aa[i][3]%gcd){
				puts("-1");
				exit(0);
			}
			xx*=aa[i][3]/gcd;
			yy*=aa[i][3]/gcd;
			int tx=aa[i][2]/gcd,ty=aa[i][1]/gcd;
			tx=abs(tx),ty=ty;
			xx=(xx%tx+tx)%tx;
			yy=(yy%ty+ty)%ty;
			return node({xx,yy,tx,ty});
		}
	}
	return node({X,Y});
}
void check(){
	int A=-1;
	for(int i=1;i<=n;i++){
		if(b[i]==1){
			if(A!=-1&&A!=a[i]){
				puts("-1");
				exit(0);
			}
		}
	}
	if(A!=-1){
		for(int i=1;i<=n;i++){
			if(A%a[i]){
				puts("-1");
				exit(0);
			}
			int B=A/a[i];
			while(B%b[i]==0)B/=b[i];
			if(B!=1){
				puts("-1");
				exit(0);
			}
		}
	}
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
	check();
	for(int i=1;i<=n;i++){
		int x=a[i];
		for(int j=2;j*j<=x;j++){
			if(x%j==0){
				p[++tot]=j;
				while(x%j==0)x/=j;
			}
		}
		if(x>1)p[++tot]=x;
		x=b[i];
		for(int j=2;j*j<=x;j++){
			if(x%j==0){
				p[++tot]=j;
				while(x%j==0)x/=j;
			}
		}
		if(x>1)p[++tot]=x;
	}
	sort(p+1,p+tot+1);
	tot=unique(p+1,p+tot+1)-p-1;
	for(int i=1;i<=n;i++){
		int k=a[i];
		for(int j=2;j*j<=k;j++){
			if(k%j==0){
				int l=lower_bound(p+1,p+tot+1,j)-p;
				while(k%j==0)k/=j,x[i][l]++;
			}
		}
		if(k>1)x[i][lower_bound(p+1,p+tot+1,k)-p]++;
		k=b[i];
		for(int j=2;j*j<=k;j++){
			if(k%j==0){
				int l=lower_bound(p+1,p+tot+1,j)-p;
				while(k%j==0)k/=j,y[i][l]++;
			}
		}
		if(k>1)y[i][lower_bound(p+1,p+tot+1,k)-p]++;
	}
	for(int i=1;i<n;i++){
		for(int j=1;j<=tot;j++){
			aa[j][1]=y[i][j];
			aa[j][2]=-y[i+1][j];
			aa[j][3]=x[i+1][j]-x[i][j];
		}
		node sss=G();
		if(sss.mx==0){
			for(int j=1;j<=tot;j++)x[i][j]+=sss.x*y[i][j];
			bool f=1;
			for(int k=i+1;k<=n;k++){
				for(int j=1;j<=tot;j++){
					if(y[k][j]){
						if(x[i][j]<x[k][j]||(x[i][j]-x[k][j])%y[k][j]){
							f=0;
							j=tot+1;k=n+1;
							break;
						}
					}
					else if(x[i][j]!=x[k][j]){
						f=0;
						j=tot+1;k=n+1;
						break;
					}
				}
			}
			if(f){
				int ans=1;
				for(int j=1;j<=tot;j++)ans=ans*fpow(p[j],x[i][j])%mod;
				print(ans);
				fwrite(puf,1,ptot,stdout);
				return 0;
			}
			else{
				puts("-1");
				exit(0);
			}
		}
		else{
			if(sss.x==0&&x[i][1]<x[i+1][1])sss.x+=sss.mx;
			for(int j=1;j<=tot;j++)x[i][j]+=sss.x*y[i][j];
			for(int j=1;j<=tot;j++)y[i][j]*=sss.mx;
			for(int j=1;j<=tot;j++)x[i+1][j]=x[i][j],y[i+1][j]=y[i][j];
		}
	}
	int ans=1;
	for(int j=1;j<=tot;j++)ans=ans*fpow(p[j],x[n][j])%mod;
	print(ans);
	fwrite(puf,1,ptot,stdout);
	return 0;
}

随机数列

过了再补,等一手高手的题解。

AT_tenka1_2019_e Polynomial Divisors

给定 N 次多项式 f(x)=aN×xN+aN1×xN1++a0 ,请求出所有满足以下条件的质数 p :对于任意整数 xf(x) 都是 p 的倍数。从小到大输出。


注意到对于一个质数 p,有 aixiaiximod(p1)(modp),所以我们可以 O(n)f(x) 转化为一个 min(n,p2) 次的多项式并判断其是否符合要求。

接下来我们对 a0 的值进行分讨:

  • a00,则 f(0)=a0,所以符合条件的只有 a0 的质因子。
  • a0=0,那我们设 g=gcdi=1nai,显然所有 g 是质因子是满足条件的。同时对于一些小于 n+1 的质数也有可能通过降幂改变系数让 p 是满足条件的。所以我们再暴力判断小于 n+1 的质数,并把合法的加入答案。

#include<bits/stdc++.h>
#define int long long
using namespace std;
char buf[1000005],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
int read(){
	int x=0,f=0,c=gc();
	while(!isdigit(c))f|=(c=='-'),c=gc();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=gc();
	return f?-x:x;
}
char puf[1000005];
int ptot;
#define pc(x) (ptot==1000000?(fwrite(puf,1,1000000,stdout),ptot=0,puf[ptot++]=x):puf[ptot++]=x)
void print(int x){
	if(x<0){
		pc('-');
		print(-x);
		return;
	}
	if(x>9)print(x/10);
	pc(x%10+'0');
}
int n,a[10005],b[10005];
vector<int>ans;
void ch(int x){
	for(int i=0;i<=min(x,n);i++)b[i]=0;
	for(int i=0;i<=n;i++)b[i%(x-1)]=(b[i%(x-1)]+a[i]%x+x)%x;
	for(int i=0;i<=min(x,n);i++){
		if(b[i])return;
	}
	ans.push_back(x);
}
int p[10005],tot;
bool f[100005];
void ss(){
	for(int i=2;i<=10000;i++){
		if(!f[i])p[++tot]=i;
		for(int j=1;i*p[j]<=10000&&j<=tot;j++){
			f[i*p[j]]=1;
			if(i%p[j]==0)break;
		}
	}
}
signed main()
{
	n=read();
	for(int i=n;i>=0;i--)a[i]=read();
	if(a[0]!=0){
		int x=abs(a[0]);
		for(int i=2;i*i<=x;i++){
			if(x%i==0){
				ch(i);
				while(x%i==0)x/=i;
			}
		}
		if(x>1)ch(x);
	}
	else{
		ss();
		int gcd=0;
		for(int i=0;i<=n;i++)gcd=__gcd(gcd,a[i]);
		gcd=abs(gcd);
		for(int i=2;i*i<=gcd;i++){
			if(gcd%i==0){
				if(i>n)ans.push_back(i);
				while(gcd%i==0)gcd/=i;
			}
		}
		if(gcd>1&&gcd>n)ans.push_back(gcd);
		for(int i=1;i<=tot&&p[i]<=n;i++){
			ch(p[i]);
		}
		sort(ans.begin(),ans.end());
	}
	for(int x:ans)print(x),pc(10);
	fwrite(puf,1,ptot,stdout);
	return 0;
}

[WC2021] 斐波那契

众所周知,小葱同学擅长计算,尤其擅长计算组合数。但是对组合数有了充分研究的小葱同学对组合数失去了兴趣,而开始研究数列。

我们定义 F0=aF1=bFi=(Fi1+Fi2)modmi2)。

现在给定 n 组询问,对于每组询问请找到一个最小的整数 p,使得 Fp=0


下午再写。

跳跳棋

跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有三颗棋子,分别在 a,b,c 这三个位置。我们要通过最少的跳动把他们的位置移动成 x,y,z(注意:棋子是没有区别的)。

跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过一颗棋子。

写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。

qhlkjrig.png


容易发现一个局面下最多只有 3 种不同的操作选择:将中间的向两边跳或者将某一边的棋子向中间跳。

常识告诉我们,这表明从一个局面出发能到达的所有局面构成一颗二叉树,而根代表的局面就是满足 ba=cb 的一个局面。

然后按题意模拟去找初末局面的 LCA 即可。


#include<bits/stdc++.h>
#define int long long
using namespace std;
char buf[1000005],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
int read(){
	int x=0,f=0,c=gc();
	while(!isdigit(c))f|=(c=='-'),c=gc();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=gc();
	return f?-x:x;
}
char puf[1000005];
int ptot;
#define pc(x) (ptot==1000000?(fwrite(puf,1,1000000,stdout),ptot=0,puf[ptot++]=x):puf[ptot++]=x)
void print(int x){
	if(x<0){
		pc('-');
		print(-x);
		return;
	}
	if(x>9)print(x/10);
	pc(x%10+'0');
}
int a,b,c,x,y,z;
deque<string>q1,q2;
string get(int a,int b,int c){
	return to_string(a)+"#"+to_string(b)+"#"+to_string(c);
}
string Get(int a,int b,int c,int l){
	while(b-a!=c-b){
		int l1=b-a,l2=c-b;
		if(l1<l2){
			int l3=(l2-1)%l1+1;
			int tt=(l2-l3)/l1;
			if(l<=tt){
				l3=l2-l*l1;
				b=c-l3;
				a=b-l1;
				break;
			}
			l-=tt;
			b=c-l3;
			a=b-l1;
		}
		else{
			int l3=(l1-1)%l2+1;
			int tt=(l1-l3)/l2;
			if(l<=tt){
				l3=l1-l*l2;
				b=a+l3;
				c=b+l2;
				break;
			}
			l-=tt;
			b=a+l3;
			c=b+l2;
		}
	}
	return get(a,b,c);
}
signed main()
{
	a=read(),b=read(),c=read(),x=read(),y=read(),z=read();
	if(a>b)swap(a,b);
	if(b>c)swap(b,c);
	if(a>b)swap(a,b);
	if(b>c)swap(b,c);
	if(x>y)swap(x,y);
	if(y>z)swap(y,z);
	if(x>y)swap(x,y);
	if(y>z)swap(y,z);
	int A=a,B=b,C=c,X=x,Y=y,Z=z;
	q1.push_front(get(a,b,c));
	int S=0,T=0;
	while(b-a!=c-b){
		int l1=b-a,l2=c-b;
		if(b-a<c-b){
			int l3=(l2-1)%l1+1;
			S+=(l2-l3)/l1;
			b=c-l3;
			a=b-l1;
			l2=l3;
		}
		else{
			int l3=(l1-1)%l2+1;
			S+=(l1-l3)/l2;
			b=a+l3;
			c=b+l2;
			l1=l3;
		}
		q1.push_front(get(a,b,c));
	}
	q2.push_front(get(x,y,z));
	while(y-x!=z-y){
		int l1=y-x,l2=z-y;
		if(y-x<z-y){
			int l3=(l2-1)%l1+1;
			T+=(l2-l3)/l1;
			y=z-l3;
			x=y-l1;
			l2=l3;
		}
		else{
			int l3=(l1-1)%l2+1;
			T+=(l1-l3)/l2;
			y=x+l3;
			z=y+l2;
			l1=l3;
		}
		q2.push_front(get(x,y,z));
	}
	if(q1[0]!=q2[0])puts("NO");
	else{
		int l=0,r=min(S,T),mid,res=S+T;
		while(l<=r){
			mid=l+r>>1;mid))l=mid+1,res=S-mid+T-mid;
			else r=mid-1;
		}
		pc('Y'),pc('E'),pc('S'),pc(10);
		print(res);
	}
	fwrite(puf,1,ptot,stdout);
	return 0;
}

小凯的疑惑

给定正整数 a,b 满足 (a,b)=1,求最大的 n 满足 ax+by=n 不存在非负整数解。


简单题。

因为 ax+by=n,所以 nax(modb),因为前面的要求,我们有 n<ax,那么有 n=axb,为了让 n 最大,我们取 x=b1,有 n=abab


#include<bits/stdc++.h>
#define int long long
using namespace std;
char buf[1000005],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
int read(){
	int x=0,f=0,c=gc();
	while(!isdigit(c))f|=(c=='-'),c=gc();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=gc();
	return f?-x:x;
}
char puf[1000005];
int ptot;
#define pc(x) (ptot==1000000?(fwrite(puf,1,1000000,stdout),ptot=0,puf[ptot++]=x):puf[ptot++]=x)
void print(int x){
	if(x<0){
		pc('-');
		print(-x);
		return;
	}
	if(x>9)print(x/10);
	pc(x%10+'0');
}
int a,b;
signed main()
{
	a=read();b=read();
	if(a==1||b==1)puts("0");
	else print(a*b-a-b);
	fwrite(puf,1,ptot,stdout);
	return 0;
}

AGC063D Many CRT

给定系数 N,a,b,c,d (2N106,1a,b,c,d106),询问同余方程组 xa+kb (mod c+kd) 的最小非负整数解 x,其中 0k<N

由于答案较大,请输出答案对 998244353 取模的结果。


g=gcd(c,d)

因为 xa(modc),也就有 xa(modg)。结合 xa+ib(modc+id),可以发现如果 gb,原方程组无解。

我们设

posted @   Grisses  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
Document
点击右上角即可分享
微信分享提示