10.04 T3 数学推导

Description

Input

Output

 输出T行,每一行一个YES或NO,表示是否存在满足条件的Ai。

Sample Input

2 3 7 7 3 8 5 6

Sample Output

YES NO YES

Hint

【数据范围与约定】
 
 
 
 
很显然题目可以说成:给出n,k,询问n是否可以用k的约数家和的形式表示。
还是乱搞50分,解法:分解K的因数然后跑背包检查是否存在N
code:
 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 ,只有就可以用最短路来解决。

 
posted @ 2018-10-04 18:50  saionjisekai  阅读(35)  评论(0编辑  收藏  举报