bzoj2081: [Poi2010]Beads(hash)

博客围绕给定字符串分段问题展开。可将串按每段k个字符划分,最后不足k个则丢弃。判断段本质相同的条件是相等或翻转后相等。要找出最多本质不同段数、对应k的数目及所有k值,采用暴力枚举段长度,用hash+set统计答案,复杂度为两只log。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
世纪水题???(大雾
题意:
给定一个串。你可以把这个串分成若干段,每块都有k个字符。(如果最后一段字符数少于k,则丢掉不要)
对于不同的k,你能得到不同的段。两个段是本质相同的,当且仅当它们相等或将其中一个段翻转后它们相等。
你现在想知道:能够获得的最多的本质不同段数;能获得这个最大值的k的数目;能获得这个最大值的所有k。
思路:
暴力枚举段的长度,然后用 h a s h + s e t hash+set hash+set统计答案。
根据调和级数的性质复杂度是两只 l o g log log的。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
inline int read(){
    int ans=0;
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return ans;
}
const int N=2e5+5;
typedef long long ll;
typedef unsigned long long Ull;
typedef pair<Ull,int> pii;
const Ull bas=311;
set<Ull>S;
int n,a[N];
struct string_hash{
    Ull pw1[N],s1[N];
    inline void init(){
        pw1[0]=1;
        for(ri i=1;i<=n;++i)pw1[i]=pw1[i-1]*bas;
        for(ri i=1;i<=n;++i)s1[i]=s1[i-1]*bas+(Ull)a[i];
    }
    inline Ull get1(int l,int r){return s1[r]-s1[l-1]*pw1[r-l+1];}
}S1,S2;
int main(){
    n=read();
    for(ri i=1;i<=n;++i)a[i]=read();
    S1.init();
    reverse(a+1,a+n+1);
    S2.init();
    reverse(a+1,a+n+1);
    int ans=0;
    vector<int>Ans;
    for(ri cnt=0,len=1;len<=n;++len,cnt=0){
        S.clear();
        for(ri l=1,r=len;l<=n&&r<=n;l+=len,r+=len){
            if(!S.count(S1.get1(l,r)*S2.get1(n-r+1,n-l+1))){
                ++cnt;
                S.insert(S1.get1(l,r)*S2.get1(n-r+1,n-l+1));
            }
        }
        if(cnt>ans)Ans.clear(),ans=cnt;
        if(cnt==ans)Ans.push_back(len);
    }
    cout<<ans<<' '<<Ans.size()<<'\n';
    for(ri i=0;i<Ans.size();++i)cout<<Ans[i]<<' ';
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值