10.04 T3 数学推导
Description
![](https://oj.bashu.com.cn/images/2652-1.png)
Input
![](https://oj.bashu.com.cn/images/2652-2.png)
Output
Sample Input
Sample Output
![](https://oj.bashu.com.cn/images/2652-4.png)
Hint
![](https://oj.bashu.com.cn/images/2652-3.png)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 bool cmp(const long long &a,const long long &b){ 7 return a>b; 8 } 9 long long a[100005],tot,vis[10000005]; 10 long long gcd(long long a,long long b){ 11 if(a<b)swap(a,b); 12 while(a=a%b)swap(a,b); 13 return b; 14 } 15 int main(){ 16 freopen("fantasy.in","r",stdin); 17 freopen("fantasy.out","w",stdout); 18 long long num,T; 19 cin>>num>>T; 20 if(num==6){ 21 while(T--){ 22 cout<<"NO"<<'\n'; 23 } 24 return 0; 25 } 26 if(num==7){ 27 while(T--){ 28 int n,k; 29 cin>>n>>k; 30 if(n%k==0)cout<<"YES"<<'\n'; 31 else cout<<"NO"<<'\n'; 32 } 33 return 0; 34 } 35 if(num==9){ 36 while(T--){ 37 long long n,k; 38 scanf("%lld%lld",&n,&k); 39 if(n%k==0){ 40 cout<<"YES"<<'\n'; 41 continue; 42 } 43 n%=k; 44 long long temp; 45 for(long long i=2;i*i<=k;i++){ 46 if(k%i==0){ 47 temp=i; 48 break; 49 } 50 } 51 if(n==temp+k/temp)cout<<"YES"<<'\n'; 52 else cout<<"NO"<<'\n'; 53 } 54 return 0; 55 } 56 while(T--){ 57 memset(vis,0,sizeof vis); 58 tot=0; 59 long long n,k; 60 cin>>n>>k; 61 if(n%k==0){ 62 if(k==1)cout<<"NO"<<'\n'; 63 else 64 cout<<"YES"<<'\n'; 65 continue; 66 } 67 n%=k; 68 for(long long i=2;i*i<=k;i++){ 69 if(k%i==0){ 70 a[++tot]=i; 71 if(k/i!=i)a[++tot]=k/i; 72 } 73 } 74 //for(long long i=1;i<=tot;i++)cout<<a[i]<<" |||| "; 75 //cout<<'\n'; 76 sort(a+1,a+tot+1,cmp); 77 vis[0]=1; 78 // cout<<tot<<'\n'; 79 for(long long i=1;i<=tot;i++){ 80 if(vis[n])break; 81 for(long long j=a[i];j<=n;j++){ 82 if(vis[j-a[i]])vis[j]=1; 83 } 84 } 85 if(vis[n])cout<<"YES"<<'\n'; 86 else cout<<"NO"<<'\n'; 87 /* int flag=0; 88 int ans=a[1]; 89 for(int i=2;i<=tot;i++){ 90 ans=gcd(ans,a[i]); 91 flag=1; 92 break; 93 } 94 if(n%ans==0)cout<<"YES"<<'\n'; 95 else cout<<"NO"<<'\n';*/ 96 } 97 return 0; 98 }
官方正解:
Subtask1:O(N^N*K)暴力。
Subtask2:O(N!*K)暴力。发现 A 是一个 n 位的排列,因为若 A 中出现两个相同的数,那么必然会有两个点一直 2 处在同一位置,在同一时间不能到达各自的家乡。
Subtask3、4、5:在一个排列转换中,将 i 向 Ai 连边,其实就可以看成是若干个环,一个环上的人会在环长的若干倍数天里回到家乡。那么题目其实就是求解是否存在一些大于 1 的整数ci,使得 lcm(ci)|k 且∑ci=n。lcm(ci)|k 等价于 ci|k,提取系数,题目要求等价于对于 ci|k,ci>1,是否存在非负整数 ti 使得∑(ti*ci)=n。对于一个不是质数的数 ci,可以将其拆成若干 k的质因数的乘积,那么题目要求可以进一步地看作是对于 k 的质因数 ci,判断存在非负整数ti 使得∑(ti*ci)=n。那么我们就得到了一个 O(T*(√K+N*logK))的类似于 DP 的做法,判断每一个数是否可以被组成。
Subtask6:k=1,由于 Ai 不等于 i,所以不可能一天回到家乡,输出 NO 即可。
Subtask7、8:k 的质因数分解只有一个数,直接判断 n 能否整除 k 即可。
Subtask9:k的质因数分解最多两个数,用exgcd判断是否存在非负整数解即可。从这个subtask开始,复杂度在于 k 上,而不同的 k 只有 50 个,可以一起处理。
Subtask10、11、12:subtask10 的部分分给了我们很大的启发。考虑这么一件事,可以用除了最小质因数 x 以外的质因数构造出 T,使得 T≡n(mod x),那么在 T 小于等于 n 的基础上加上一些 x 即可使和为 n。如何判断是否存在这样的 T,在模意义下,将 t 向 t+ci 连边,边权为 ci,跑单源最短路,求出的 0~n%x 的最短路的长度即为上文需要的 T。
Subtask13、14:发现 k 达到了 10^15,那么最小质因数可以达到 3*10^7,跑不了最短路。
考虑将质因数个数为 2 的用 subtask9 特判掉,那么最小质因数最大就在三次根号下 10^15,即为 10^5。同时,因为质因数分解过慢,要预处理√10^15 内的质数。
题解
很显然,只有k的质因子是有用的, 分类讨论, 如果只有一个质因子,那么就直接取模。 如果只有两个质因子,那么就扩展欧几里得。 如果超过三个质因子,那么最小的一个质因子就一定小于10^5 ,只有就可以用最短路来解决。