牛客 - 牛牛的最大兴趣组(思维+数论)
Posted Frozen_Guardian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客 - 牛牛的最大兴趣组(思维+数论)相关的知识,希望对你有一定的参考价值。
题目链接:点击查看
题目大意:给出 n n n 个数,要求选出最多的数,使得任意两个数的乘积不能是三次平方数,三次平方数,诸如 2 3 = 8 , 3 3 = 27 2^3=8,3^3=27 23=8,33=27
题目分析:这个模型和前两天 c f cf cf 上遇到的一个样:CodeForces - 1497E2
这个模型的特点就是,假设要使得任意两个数的乘积不能是 m m m 次平方数的话,当我们将所有的数字中,出现次数为 m m m 的倍数的质因子都筛掉后,剩下的数一定会两两匹配。具体到某个质因子来说,假设其出现次数为 x x x,那么其需要和出现次数为 m − x m-x m−x 的数字匹配,其乘积后该质因子的出现次数才能被 m m m 整除
下面就将本模型扩展到 m m m 次平方数来思考如何求解
又因为经过上述的处理后,除了 1 1 1 之外,其他的数都有且仅有唯一一个数字与其匹配。也就是对于每个数来说,只有一个数字与其乘积会不满足条件,所以这两个数字肯定不能同时出现。既然如此贪心去想的话,保留出现次数较多的那个数字显然是最优的
到此本题的思路就已经解决了,现在留下来两个难点需要我们逐个思考:
- 如何筛掉出现次数为 m m m 倍的质因子
- 在筛完的数字中,如何快速找到与其对应的那个数字,即满足两个数的乘积是 m m m 次平方数
针对第一点,最朴素的做法就是 O ( n ) O(\\sqrt{n}) O(n) 的唯一分解定理了,对于出现次数可以被 m m m 整除的质因子直接舍弃即可,可惜时间复杂度不允许
然后考虑也比较常用的,预处理出每个数字的最小质因子,然后 O ( l o g n ) O(logn) O(logn) 去筛质因子,这种实现时间复杂度可行,但是我们无法预处理到 2 e 9 2e9 2e9 的数据范围
其实我们不难发现,并不需要枚举出所有的质因子然后判断,我们的目标是为了筛掉所有 m m m 次平方数的数字,换句话说我们可以预处理出 m m m 次平方的数字,然后用这些数字去筛即可,而这样的 m m m 次平方数,最多有 2 e 9 m \\sqrt[m]{2e9} m2e9 个数字,又因为合数总是可以被分解成质数,所以我们只需要保留质因子即可,这样有效数字最终约等于 2 e 9 m l o g m \\frac{\\sqrt[m]{2e9}}{logm} logmm2e9,针对本题而言,当 m = 3 m=3 m=3 的时候,这个数值约等于 200 200 200
那么还剩下一个问题,如何找到对应的数字呢,实际上最朴素的做法还是 O ( n ) O(\\sqrt{n}) O(n) 去唯一分解这个数,如果对于某个质因子 p p p,其出现次数是 x x x,那么其对应的那个数字, p p p 的出现次数一定是 m − x m-x m−x
到此为止,网上的部分题解,都是 O ( n n ) O(n\\sqrt{n}) O(nn) 实现的,因为本题数据水了,所以给放过去了,其实一组很简单的 h a c k hack hack 样例就是, n n n 个 1 e 9 + 7 1e9+7 1e9+7,直接就把复杂度卡成至少 1 e 5 ∗ 1 e 4 = 1 e 9 1e5*1e4=1e9 1e5∗1e4=1e9 起步了
所以该如何去查找对应的那个数字呢,假设我们要找 x x x 对应的数字,只需要将 x x x 变成 x m − 1 x^{m-1} xm−1,然后再将出现次数为 m m m 的质因子筛掉即可,具体原理就是:对于任意两个相邻的自然数来说,都是互质的,所以 g c d ( m − 1 , m ) = 1 gcd(m-1,m)=1 gcd(m−1,m)=1,对于公式 a + a ∗ ( m − 1 ) = a ∗ m a+a*(m-1)=a*m a+a∗(m−1)=a∗m,两边同时对 m m m 取模得到 a + a ∗ ( m − 1 ) = 0 ∣ m a+a*(m-1)=0|m a+a∗(m−1)=0∣m,所以将 x x x 变为 x m − 1 x^{m-1} xm−1 然后再将出现次数为 m m m 的倍数的质因子筛掉就可以找到对应的数字了
总的时间复杂度就是 O ( n 2 e 9 m ) O(n\\sqrt[m]{2e9}) O(nm2e9),对于本题而言就是 O ( 200 ∗ n ) O(200*n) O(200∗n),实现的时候我套了个 m a p map map ,理论上说应该是不可以的,但因为本题数据水,所以是可以过的,如果是正解的话我感觉需要打个哈希降一下复杂度,大概就是 unordered_map 了
代码:
// Problem: 牛牛的最大兴趣组
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/7604/C
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
T f=1;x=0;
char ch=getchar();
while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
x*=f;
}
template<typename T>
inline void write(T x)
{
if(x<0){x=~(x-1);putchar('-');}
if(x>9)write(x/10);
putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e6+100;
bool vis[N];
vector<int>pri;
map<LL,int>mp;
void init() {
for(int i=2;i<=2000;i++) {
if(vis[i]) {
continue;
}
for(int j=i+i;j<=2000;j+=i) {
vis[j]=true;
}
}
for(int i=2;1LL*i*i*i<=2e9;i++) {
if(!vis[i]) {
pri.push_back(i*i*i);
}
}
}
LL change(LL x) {
for(auto it:pri) {
while(x%it==0) {
x/=it;
}
}
return x;
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
int n;
read(n);
for(int i=1;i<=n;i++) {
LL x;
read(x);
mp[change(x)]++;
}
int ans=0;
for(auto &it:mp) {
if(it.first==1) {
ans++;
continue;
}
LL rk=change(it.first*it.first);
if(!mp.count(rk)) {
ans+=it.second;
} else {
ans+=max(it.second,mp[rk]);
it.second=mp[rk]=0;
}
}
cout<<ans<<endl;
return 0;
}
以上是关于牛客 - 牛牛的最大兴趣组(思维+数论)的主要内容,如果未能解决你的问题,请参考以下文章