1. 程式人生 > >Codeforces Round #511 (Div. 2) C

Codeforces Round #511 (Div. 2) C

題意:

給你n個數,然後讓你刪掉一些數使得剩下的數的gcd比原來的gcd大,並且要使刪掉的數的數量最少

最後輸出需要刪掉的數的數量

解析:

大致有兩種做法,我用的是將每一個數質因數分解

這個分解用的是線性篩,因為線性篩中遍歷到每一個合數時都是通過該合數最小的質因數*某一個數

得到的,那麼我們就用mu[]記錄每一個數最小的質因數,然後遞迴地分解這個數就可以了

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15000000+10;
const int MAXN = 3e5+10;

bool vis[N];
int mu[N];
int prime[MAXN*5],cnt;
int a[MAXN];
int b[MAXN];
int num[N];
int maxx;
int maxgcd;

inline void solve()
{
    //memset(mu,0,sizeof(mu));
    cnt = 0;
    vis[0]=vis[1]=true;
    for(int i=2; i<N; i++)
    {

        if(!vis[i])   //vis用於標記是否是非素數,若是素數則為false
        {
            prime[cnt++] = i;   //i=d
        }

        for(int j=0; j<cnt&&i*prime[j]<N; j++)
        {
            vis[i*prime[j]] = 1;
            if(i%prime[j]) mu[i*prime[j]] = prime[j];   //如果這個合數(暫且說成合數,質數的情況同理)不能被prime[j]整除,prime[j]肯定不是i的因子(換句話說i中沒有與prime[j]相同的因子)
            else    //如果i%prime[j]==0那麼i肯定可以分解成prime[j]*(某一個數),這樣到之後k=i*prime[j+1]時,k肯定可以從prime[j+1]*(某一個數)*prime[j]得到(一個更大的合數*一個更小的質數得到你)
            {
                mu[i*prime[j]] = prime[j];   //如果這個合數(質數)能被prime[j]整除,那麼prime[j]就是i的因子,i*prime[j]中肯定有相同的質因子
                break;         //這樣相比與普通篩法O(n*logn*logn)就避免了重複,使得複雜度可以降到O(n)
            }
        }
    }
}

int gcd(int a,int b)
{
    int k;
    while(b)
    {
        k=a%b;
        a=b;
        b=k;
    }
    return a;
}

bool tim[N];
inline void PollardRho(int n)
{
    int flag=0;
    if(!vis[n])
    {
        num[n]++;
        maxx=max(num[n],maxx);
        return;
    }
    int u=n;
    int tmp=mu[u];
    while(vis[u]&&tmp)
    {
        if(!tim[tmp])
        {
            num[tmp]++;
            maxx=max(num[tmp],maxx);
            tim[tmp]=true;
        }
        u=u/tmp;
        tmp=mu[u];
    }
    if(!vis[u]&&!tim[u])
    {
        num[u]++;
        maxx=max(num[u],maxx);
    }
    u=n;
    tmp=mu[u];
    while(vis[u]&&tmp)
    {
        tim[tmp]=false;
        u=u/tmp;
        tmp=mu[u];
    }

}

int main()
{
    solve();
    int n;
    scanf("%d",&n);
    scanf("%d",&a[1]);
    int mg=a[1];
    for(int i=2;i<=n;i++) scanf("%d",&a[i]),mg=gcd(mg,a[i]);
    int flag=0;
    int cm=0,ans=0;
    for(int i=1;i<=n;i++)
    {
        a[i]=a[i]/mg;
        //if(a[i]!=1) flag=1;

    }

    maxx=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==1) continue;
        PollardRho(a[i]);
    }
    if(maxx==0) printf("-1\n");
    else printf("%d\n",n-maxx);
    return 0;
}

另外一種我是看standing裡的一位大佬的程式碼,他是用類似普通篩法(nlogn)列舉每一個公約數的倍數

假如原來的最大公因數是d,那麼他從d+1開始列舉每一個數x,然後列舉x的倍數,看a裡面有多少數是x的倍數

然後把是x的倍數的數都刪掉,因為這些數已經被x替代了,因為一定是x的答案更優,

a裡面是x的倍數的數>=a裡面是k*x的倍數的數(k=1,2,3,4)

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int INF =0x3f3f3f3f;
const int MAXN =1.5e7 + 10;
int pi[MAXN], a[MAXN];
int main()
{
	int n, d = 0;
	 scanf("%d",&n);
	for (int i=0; i<n; ++i)
	{
		int in;
		scanf("%d", &in);
		a[in]++;
		if (d==0)
			d = in;
		else
			d = __gcd(d,in);
	}
	int ans = n;
	for (int i=d + 1;i<MAXN; ++i)
		if (pi[i]==0)
		{
			int cnt = 0;
			for (int j = i; j < MAXN; j += i)
				pi[j] = 1, cnt += a[j];
			ans = min(ans,n-cnt);
		}
	if (ans < n) printf("%d",ans);
	else printf("-1");
	return 0;
}