【PA2013】【BZOJ3837】Filary

Description

给定n个正整数,从中挑出k个数,满足:存在某一个m(m>=2),使得这k个数模m的余数相等。
求出k的最大值,并求出此时的m。如果有多组解使得k最大,你要在此基础上求出m的最大值。
Input

第一行一个正整数n(2<=n<=10^5)。
第二行n个正整数wi。保证不会出现所有w[i]都相等的情况。
Output

一行两个整数k,m。保证答案存在。
Sample Input

6

7 4 10 8 7 1
Sample Output

5 3
HINT

Source

这题好厉害呀QAQ
首先可以发现答案k最小也得是 n2 ,感觉可以用奇怪的姿势乱搞?但是我的姿势水平并不足够想出正确的乱搞算法
然后就翻了zky的提交记录和Claris的题解QAQ
因为答案最小是 n2 ,所以这就意味着一个数至少也有 12 的概率在被选中的k个数里,所以可以设计一个随机+Hash的做法.
每次随机找一个数 ai ,将这个数作为必选的k个数之一,然后用其他数对其作差,对差值分解质因数.

我们考虑找出分解出的质因数里出现次数最多的那个数,每个数我们都可以看成是拆分为 ai+delta ,考虑对取模后的结果得贡献 ai 部分都是一样的,然后 delta 有相同质因数的,对那个质因数取模后贡献是一样的(0),所以出现次数最多的那个质因数的出现次数再加上 ai 出现的次数就是最优情况下的k.

对于那个固定的k找一个m,只需要把那些找到的所有对应位置的因数乘一下,然后看一看总共出现过哪些结果,找到最小的那个.

对于数目的统计,可以对每个数给一个随机hash值,异或起来求出hash值,然后排序扫一遍.

如果你觉得我写的口胡根本看不懂的话..Claris题解传送门
你也可以直接从QQ上找Claris问.(我怕我口胡错掉QAQ)

最后,今天是3.30,蓝月亮lct1999的生日,代码里粗线了奇怪的东西!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100010
#define MAXV 10000010
#define SIZE 700000
#define GET (ch>='0'&&ch<='9')
using namespace std;
int n,k,m,cnt,tp,T;
int a[MAXN],b[MAXN],maxn;
bool not_prime[MAXV];
int prime[SIZE],top,id[MAXV],fac[MAXV];
int vis[SIZE],sta[40],pow;
int pos[SIZE],lst[SIZE];
struct happy_birthday_lct1999
{
    int cnt,hash,num;
    happy_birthday_lct1999(){   hash=cnt=0;num=1;   }
    happy_birthday_lct1999(int Cnt,int Hash,int Num)    {   cnt=Cnt;hash=Hash;num=Num;  }
    inline bool operator <(const happy_birthday_lct1999 &a)const    {   return hash<a.hash; }
}s[SIZE];
inline void in(int &x)
{
    char ch=getchar();x=0;
    while (!GET)    ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();
}
void check()
{
    for (int i=2;i<=maxn;++i)
    {
        if (!not_prime[i])  prime[++top]=i,fac[i]=i,id[i]=top;
        for (int j=1;j<=top&&i*prime[j]<=maxn;++j)
        {
            fac[i*prime[j]]=prime[j];id[i*prime[j]]=j;not_prime[i*prime[j]]=1;
            if (i%prime[j]==0)  break;
        }
    }
}
void solve(int x,int y)
{
    int tmp=0,t=0;
    for (int i=1;i<=pow;++i)    vis[sta[i]]=0;pow=0;
    for (;x!=1;vis[id[x]]*=fac[x],x/=fac[x])    if (!vis[id[x]])    vis[sta[++pow]=id[x]]=1;
    for (int i=1;i<=pow;++i)
    {
        tmp=vis[sta[i]];
        if (lst[sta[i]]!=T) lst[sta[i]]=T,s[t=pos[sta[i]]=++tp]=happy_birthday_lct1999(0,0,tmp);
        else    t=pos[sta[i]];
        ++s[t].cnt;s[t].hash^=y;s[t].num=min(s[t].num,tmp);
    }
}
int main()
{
    in(n);s[0].hash=-1;
    for (int i=1;i<=n;i++)
    {
        in(a[i]);maxn=max(maxn,a[i]);
        while (!b[i])   b[i]=rand();
    }
    check();
    for (T=1;T<=4;++T)
    {
        int x=a[rand()%n+1],i,j=0;cnt=tp=0;
        for (i=1;i<=n;i++)
            if (a[i]!=x)    solve(max(a[i]-x,x-a[i]),b[i]);
            else    cnt++;
        sort(s+1,s+tp+1);
        for (int i=1;i<=tp;++i)
            if (s[i].hash!=s[j].hash)
            {
                if (j)
                {
                    if (s[j].cnt+cnt>k) k=s[j].cnt+cnt,m=s[j].num;
                    else    if (s[j].cnt+cnt==k&&s[j].num>m)    m=s[j].num;
                }
                j=i;
            }
            else    s[j].num*=s[i].num;
        if (s[j].cnt+cnt>k) k=s[j].cnt+cnt,m=s[j].num;
        else    if (s[j].cnt+cnt==k&&s[j].num>m)    m=s[j].num;
    }
    printf("%d %d\n",k,m);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值