哈希+组合数学+思维

Posted 钟钟终

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哈希+组合数学+思维相关的知识,希望对你有一定的参考价值。

B. Password

做法有很多,kmp、哈希都可以。wa麻了,调了2个小时才接受自己的做法有问题,固执。
思路:
1.可用哈希手法O(1)判断一个字符串的两端子串是否相同。
2.对字符串的长度进行二分,若这一段(从第二个字符~第n-1的字符)的哈希值等于首部、尾部字符串,则成立。
代码:

#include <bits/stdc++.h>
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
#define ULL unsigned long long
using namespace std;
const double eps=1e-8;
const int mod=998244353;
const int N=1e6+5;
const int P=131;
ULL p[N],h[N];
char s[N];
int a[N],n,cnt;
int init(char s[])

    p[0]=1,h[0]=0;
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
    
        p[i]=p[i-1]*P;
        h[i]=h[i-1]*P+s[i];
    
    return h[len];

//计算s[l-r]之间的hash值
ULL get(int l,int r)

    return h[r]-h[l-1]*p[r-l+1];

//判断两子串是否相同
bool subs(int l1,int r1,int l2,int r2)

    return get(l1,r1)==get(l2,r2);

bool check(int len,int val)

    for(int i=2;i+len-1<n;i++)
        if(get(i,i+len-1)==val) return 1;
    return 0;

void solve()

    cin>>(s+1);
    n=strlen(s+1);
    init(s);
    for(int i=1;i<n;i++)
    
        if(subs(1,i,n-i+1,n)) a[++cnt]=i;
    
    int l=0,r=cnt,mid=0,ans=0;
    while(l<=r)
    
        mid=(l+r)/2;
        if(check(a[mid],get(1,a[mid]))) ans=mid,l=mid+1;
        else r=mid-1;
    
    if(ans==0)
        cout<<"Just a legend"<<endl;
    else
        for(int i=1;i<=a[ans];i++) cout<<s[i];cout<<endl;

signed main()

    ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;


放一下自己tle的代码:

#include <bits/stdc++.h>
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
#define ULL unsigned long long
using namespace std;
const double eps=1e-8;
const int mod=998244353;
const int N=1e6+5;
const int P=131;
ULL pl[N],h[N];
int init(char s[])

    pl[0]=1,h[0]=0;
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
    
        pl[i]=pl[i-1]*P;
        h[i]=h[i-1]*P+s[i];
    
    return h[len];

//计算s[l-r]之间的hash值
ULL get(int l,int r)

    return h[r]-h[l-1]*pl[r-l+1];

//判断两子串是否相同
bool subs(int l1,int r1,int l2,int r2)

    return get(l1,r1)==get(l2,r2);


char s[N],s1[N],s2[N],s3[N],s4[N],ans[N];
int p[N];
void pre(char s2[],int m)

    p[1]=0;int j=0;
    for(int i=1;i<m;i++)
    
        while(j>0&&s2[j+1]!=s2[i+1])
            j=p[j];
        if(s2[j+1]==s2[i+1])
            j++;
        p[i+1]=j;
    

int kmp(char s1[],char s2[],int g)

    int n=strlen(s1+1),m=strlen(s2+1);
    //for(int i=1;i<=n;i++) cout<<s1[i];cout<<endl;
    //for(int i=1;i<=m;i++) cout<<s2[i];cout<<endl;
    pre(s2,m);
    int j=0;
    for(int i=0;i<n;i++)
    
        while(j>0&&s2[j+1]!=s1[i+1])
            j=p[j];
        if(s2[j+1]==s1[i+1]) j++;
        if(j==m)
        
            //cout<<g<<" "<<i+2-m<<endl;
            if(i+1-m!=g) return 1;
            j=p[j];
        
    
    return 0;

void solve()

    cin>>(s+1);
    int n=strlen(s+1);
    int gg=1;
    for(int i=2;i<=n;i++)
    
        if(s[i]==s[i-1]) continue;
        else gg=0;
    
    if(gg&&n>2)
    
        for(int i=1;i<=n-2;i++) cout<<s[i];cout<<endl;return;
    
    int flag=0;
    init(s);
    int g=0;
    for(int i=1,j=n;i<n;i++,j--)
    
        //cout<<s1<<" "<<s2<<endl;
        if(subs(1,i,j,n))
        
            for(int j=1;j<=i;j++) s1[j]=s[j];
            for(int j=1;j<=n-1;j++) s3[j]=s[j];
            //s1=s.substr(1,i);
            //s3=s.substr(1,n-1);
            //cout<<s1<<" "<<s3<<endl;
            if(kmp(s3,s1,0)==1)
            
                //cout<<"11  "<<s3<<" "<<s1<<endl;
                flag=1;
                for(int j=1;j<=i;j++) ans[j]=s1[j],g=i;
            
            for(int j=2;j<=n;j++) s4[j]=s[j];
            //s4=s.substr(2,n-1);
            if(kmp(s4,s1,n-i-1)==1)
            
                //cout<<"22  "<<s4<<" "<<s1<<endl;
                flag=1;for(int j=1;j<=i;j++) ans[j]=s1[j],g=i;
            
        
    
    if(flag)
    
        for(int i=1;i<=g;i++) cout<<ans[i];
        cout<<endl;
    
    else
        cout<<"Just a legend"<<endl;

signed main()

    ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;


C. Card Game

这道C题比其他很多C都要难一点感觉。主要围绕最大牌进行讨论,利用组合数学算出方案数,也可用dp来写。

#include <bits/stdc++.h>
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
using namespace std;
const double eps=1e-8;
const int mod=998244353;
const int N=1e6+5;
int n,f[66],fac[66],inv[66];
int qpow(int x,int y)

    int res=1;
    while(y)
    
        if(y&1) res=res*x%mod;
        x=x*x%mod;y>>=1;
    
    return res;

int getinv(int x) return qpow(x,mod-2);
int C(int n,int m)

    return fac[n]*inv[m]%mod*inv[n-m]%mod;

void solve()

    cin>>n;
    cout<<f[n]<<" "<<(C(n,n/2)-1-f[n]+mod)%mod<<" "<<1<<endl;

signed main()

    //ios;
    fac[0]=1;
    for(int i=1;i<=60;i++) fac[i]=fac[i-1]*i%mod;
    inv[60]=getinv(fac[60]);
    for(int i=60;i>=1;i--) inv[i-1]=inv[i]*i%mod;
    f[2]=1;f[4]=3;
    for(int i=6;i<=60;i+=2)
    
        //cout<<C(i,i/2)<<endl;
        f[i]=((C(i-1,i/2-1)+C(i-4,i/2-1))%mod+f[i-4])%mod;
        //cout<<f[i]<<endl;
    
    int T;cin>>T;
    while(T--)
        solve();
    return 0;


B. Playing with GCD

这个B wa了好几次,还是总结一下吧。
1.刚开使思考到的点:前一个数必须大于等于下一个数,此外取模结果要为0.没把它定义为一个构造题。
2.然后发现了一组样例:a[i]:16 5,此刻b[i]:16 80 5最优。发现了最小公倍数的关系,再按照题意进行构造。若发现gcd(b[i-1],b[i])!=a[i-1],则不成立。

#include <bits/stdc++.h>
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
#define ULL unsigned long long
using namespace std;
const double eps=1e-8;
const int mod=998244353;
const int N=1e6+5;
const int P=131;
int n,a[N],b[N];

void solve()

    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    b[1]=a[1];;
    int flag=以上是关于哈希+组合数学+思维的主要内容,如果未能解决你的问题,请参考以下文章

[思维] aw3779. 相等的和(思维+构造+题意理解+aw周赛009_2)

2022牛客寒假算法基础集训营6 全部题解

2022牛客寒假算法基础集训营6 全部题解

CF1419ABCD 思维+数学

2022牛客寒假算法基础集训营6题解 ABCDEFGHIJ

HDU1099---数学 | 思维