CF1468L - Prime Divisors Selection

题目大意

对于一个序列A,一个合法的质因子序列P满足Pi|Ai,Pi is a prime

给定一个序列ai,i[1,n],求选出k个数,使得对于选出的序列A

不存在一个P使得P中某个元素恰好出现一次

n1000,ai1018


分析

由题目的意思我们知道肯定要分解质因数

Pollard’s_Rho!!!

1018分解质因数可不是开玩笑的。。。

所以先考虑合法A的判定

判定A合法

假设可以存在一个元素恰好出现一次x,那么在A所有元素质因子中至少要包含一个x

并且,不存在两个元素只包含x

也就是说,对于合法的A中出现的所有质因子x,都必须存在两个元素只包含x

我们称只包含x作为质因子的元素为x-元素,为了构造合法的A,我们必须对于一些x选出若干对x-元素

对于每个x,我们只把前两个x元素视为有效,假设有c对这样的元素

那么情况分几种

1.2|k,2ck,那么直接随意选完即合法

2.2ck,2|k,此时我们需要选出部分对,使得剩下的元素中存在一个数它的因子集已经被选

枚举剩下一个元素,判定合法即可

3.2ck,此时可以将c对全部选出,判断是否还存在k2c个可以选择即可


质因数分解

亲身试验,我的Pollards_Rho它T飞了

容易发现,对于x元素我们只需要找到它的ai=xk

对于其他元素我们只需要找到ai对应的x的集合,或者判断无法被x元素集合包含

由于n1000,我们可以先得到x元素集合,其他元素我们最后一个个判定

找到ai=xk问题简化了很多

如果你相信std::pow,可以直接来

只需要找到一个最小的k,使得ai=xk,判定x是否为质数,如果是则停止,否则继续分解x

对于k3,甚至更大一些的情况,std::pow比较可信

k>3的情况(实际上k=4k=2包含,所以是k5

实际上x已经很小了,直接枚举质数即可

素数判定依然需要Miller_Rabin但是至少不用Pollards_Rho了

CodeForces Submission

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
const int N=1e5+10;

int n,m;
int pri[N],pc,notpri[N];

ll qmul(ll x,ll y,ll P){
ull z=(long double)x/P*y+0.5;
ll res=(ull)x*y-z*P; Mod2(res);
return res;
}
ll qpow(ll x,ll k,ll P){
ll res=1;
for(;k;k>>=1,x=qmul(x,x,P)) if(k&1) res=qmul(res,x,P);
return res;
}

int Miller_Rabin(ll n){
if(n<N) return !notpri[n];
if(~n&1) return 0;
ll s=n-1,t=0;
while(s%2==0) s/=2,t++;
rep(k,1,7) {
ll a=qpow(pri[rand()%pc+1],s,n),b;
rep(i,1,t) {
b=qmul(a,a,n);
if(b==1 && a!=1 && a!=n-1) return 0;
a=b;
}
if(a!=1) return 0;
}
return 1;
}

ll a[N],mk[N];
vector <ll> F[N]; // Factor Set of each element
vector <ll> IF; // Independent Factor Set
void unique(vector <ll> &a){ sort(a.begin(),a.end()),a.erase(unique(a.begin(),a.end()),a.end()); }

map <ll,vector<int> > M;
ll ans[N];
void Outp(){
rep(i,1,m) ans[i]=a[ans[i]];
sort(ans+1,ans+m+1);
rep(i,1,m) printf("%lld ",ans[i]);
exit(0);
}

ll Root2(ll n){
ll x=round(sqrt(n));
return x*x==n?x:-1;
}
ll Root3(ll n){
ll x=round(pow(n,1./3));
return x*x*x==n?x:-1;
}

ll KDivide(ll x){
if(Miller_Rabin(x)) return x;
ll y;
if(~(y=Root2(x))) return KDivide(y);
if(~(y=Root3(x))) return KDivide(y);
ll U=pow(x,1./5)+4;
vector <ll> fac;
for(int i=1;pri[i]<=U;++i) if(x%pri[i]==0) {
while(x%pri[i]==0) x/=pri[i];
fac.pb(pri[i]);
}
if(fac.size()==1 && x==1) return fac[0];
return -1;
}


int main(){
rep(i,2,N-1) if(!notpri[i]) {
pri[++pc]=i;
for(int j=i+i;j<N;j+=i) notpri[j]=1;
}
n=rd(),m=rd();
rep(i,1,n) {
ll x=KDivide(a[i]=rd<ll>());
if(~x) IF.pb(x);
}
unique(IF);
rep(i,1,n) {
ll x=a[i];
for(ll y:IF) if(x%y==0) {
while(x%y==0) x/=y;
F[i].pb(y);
}
if(x>1) F[i].pb(-1),F[i].pb(-2); // invalid factor, emm... to avoid some situation we push two
if(F[i].size()==1 && M[F[i][0]].size()<2) M[F[i][0]].pb(i),mk[i]=1;
}
int c=0;
for(auto i:M) if(i.second.size()>=2) c++;
if(m%2==0 && c*2>=m) {
// choose k/2 pairs!!
int k=m;
for(auto i:M) if(i.second.size()>=2) {
if(!k) break;
rep(j,0,1) ans[k--]=i.second[j];
}
Outp();
}
if(c*2>=m) {
// find another
rep(i,1,n) if(!mk[i] && (int)F[i].size()<=m/2) {
int f=1;
for(ll x:F[i]) if(M[x].size()<2) f=0;
if(f) {
int k=m;
ans[k--]=i;
for(ll x:F[i]) {
rep(j,0,1) ans[k--]=M[x][j];
M.erase(x);
}
for(auto i:M) if(i.second.size()>=2) {
if(!k) break;
rep(j,0,1) ans[k--]=i.second[j];
}
Outp();
}
}
} else {
int k=m;
for(auto i:M) if(i.second.size()>=2) {
if(!k) break;
rep(j,0,1) ans[k--]=i.second[j];
}
// Count if we have left much enough...
rep(i,1,n) if(!mk[i]) {
if(!k) break;
int f=1;
for(ll x:F[i]) if(M[x].size()<2) f=0;
if(f) ans[k--]=i;
}
if(!k) Outp();
}
puts("0");
}