bzoj 2081: [Poi2010]Beads 哈希

题意

Zxl有一次决定制造一条项链,她以非常便宜的价格买了一长条鲜艳的珊瑚珠子,她现在也有一个机器,能把这条珠子切成很多块(子串),每块有k(k>0)个珠子,如果这条珠子的长度不是k的倍数,最后一块小于k的就不要拉(nc真浪费),保证珠子的长度为正整数。 Zxl喜欢多样的项链,为她应该怎样选择数字k来尽可能得到更多的不同的子串感到好奇,子串都是可以反转的,换句话说,子串(1,2,3)和(3,2,1)是一样的。写一个程序,为Zxl决定最适合的k从而获得最多不同的子串。 例如:这一串珠子是: (1,1,1,2,2,2,3,3,3,1,2,3,3,1,2,2,1,3,3,2,1), k=1的时候,我们得到3个不同的子串: (1),(2),(3) k=2的时候,我们得到6个不同的子串: (1,1),(1,2),(2,2),(3,3),(3,1),(2,3) k=3的时候,我们得到5个不同的子串: (1,1,1),(2,2,2),(3,3,3),(1,2,3),(3,1,2) k=4的时候,我们得到5个不同的子串: (1,1,1,2),(2,2,3,3),(3,1,2,3),(3,1,2,2),(1,3,3,2)
n<=200000,a[i]<=n

分析

若我们暴力枚举所有子串的话,复杂度为 n1+n2+...+nn=n(ln(n)+c)
c为欧拉常数,貌似约等于0.5,所以说这个复杂度是可以过的。
那么我们双哈希暴力枚举即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;

const int N=200005;
const LL MOD1=5971849;
const LL MOD2=1000037;

int n,cnt,sum,a[N];
LL last[MOD1],hash1[N],hash2[N],mi1[N],mi2[N],Hash1[N],Hash2[N];
struct edge{LL y;int next;}e[N*40];
vector<int> ans;
vector<LL> w;

void addedge(LL x,LL y,LL X,LL Y)
{
    int flag=0;
    for (int i=last[x];i;i=e[i].next)
        if (e[i].y==y)
        {
            flag=1;break;
        }
    if (!flag)
    {
        sum++;
        if (!last[x]) w.push_back(x);
        e[++cnt].y=y;e[cnt].next=last[x];last[x]=cnt;
        if (!last[X]) w.push_back(X);
        e[++cnt].y=Y;e[cnt].next=last[X];last[X]=cnt;
    }
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    mi1[0]=mi2[0]=1;
    for (int i=1;i<=n;i++) mi1[i]=(LL)mi1[i-1]*(n+1)%MOD1,mi2[i]=(LL)mi2[i-1]*(n+1)%MOD2;
    for (int i=1;i<=n;i++)
    {
        hash1[i]=((LL)hash1[i-1]*(n+1)+a[i])%MOD1;
        hash2[i]=((LL)hash2[i-1]*(n+1)+a[i])%MOD2;
    }
    for (int i=n;i>=1;i--)
    {
        Hash1[i]=((LL)Hash1[i+1]*(n+1)+a[i])%MOD1;
        Hash2[i]=((LL)Hash2[i+1]*(n+1)+a[i])%MOD2;
    }
    int mx=0,tot=0;
    for (int i=1;i<=n;i++)
    {
        sum=0;
        for (int j=1;j<=n&&j+i-1<=n;j+=i)
        {
            LL x=(hash1[j+i-1]-hash1[j-1]*mi1[i]%MOD1+MOD1)%MOD1,y=(hash2[j+i-1]-hash2[j-1]*mi2[i]%MOD2+MOD2)%MOD2;
            LL X=(Hash1[j]-Hash1[j+i]*mi1[i]%MOD1+MOD1)%MOD1,Y=(Hash2[j]-Hash2[j+i]*mi2[i]%MOD2+MOD2)%MOD2;
            addedge(x,y,X,Y);
        }
        if (sum>mx)
        {
            mx=sum;tot=1;
            ans.clear();ans.push_back(i);
        }
        else if (sum==mx)
        {
            tot++;ans.push_back(i);
        }
        cnt=0;
        for (vector<LL>::iterator it=w.begin();it!=w.end();it++) last[*it]=0;
        w.clear();
    }
    sort(ans.begin(),ans.end());
    printf("%d %d\n",mx,tot);
    for (int i=0;i<tot-1;i++) printf("%d ",ans[i]);
    printf("%d",ans[tot-1]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值