2019杭电多校第五场

有两个题队友补的,先安利一波

2019hdu暑假多校第五场1004 equation HDU - 6627

2019hdu暑假多校第五场1007 permutation 2 HDU - 6630

1005 permutation 1 HDU 6628

题意:给你一个n和k,求n的全排列中,相邻两数的差值的数组字典序第k小的排列

思路:其实就是两个字,暴力.......

字典序第k小,k的范围是n!跟10000取min,8!=40320,是第一个大于10000的数,所以当n小于等于8时,使用next_permutation函数暴力找到,再按照差值排个序就好了

对于n大于8的情况因为只需要10000位,所以只需要全排列8个位子的数字就好了,由于差值字典序最小,那么我们把n放第一位,剩下的从1到n-9这些数字在n后边依次放好即可,最后把n-8到n-1这个8个数全排列一下找到第k个即可

T组询问,担心超时可以先预处理把他们都存起来,最后直接查询就ok了

#include<bits/stdc++.h>
using namespace std;
#define ll long long

struct node
{
    int a[50005][15];
    struct Node{
        int z[15],pos;
        bool operator < (const Node &x)const{
            for(int i=1;i<=7;i++){
                if(z[i]!=x.z[i]) return z[i]<x.z[i];
            }
        }
    }b[50005];
}ans[25];

int main()
{
	freopen("1.in","r",stdin); 
    int t;scanf("%d",&t);
    for(int i=2;i<=20;i++){
        int tmp[15];
        //if(i==9) continue;
        if(i<9){

            int cnt = 0;
            for(int j=1;j<=i;j++) tmp[j]=j;//cout<<i<<endl;
            do{
                cnt++;
                for(int j=1;j<=i;j++){
                    ans[i].a[cnt][j]=tmp[j];
                    if(j!=1) ans[i].b[cnt].z[j-1]=tmp[j]-tmp[j-1];
                }
                ans[i].b[cnt].pos=cnt;
            }while(next_permutation(tmp+1,tmp+1+i));
            sort(ans[i].b+1,ans[i].b+cnt+1);
        }
        else{//cout<<1<<endl;
            int cnt = 1;
            for(int j=1;j<=8;j++) tmp[j]=i-9+j;
            do{
                for(int j=1;j<=8;j++){
                    ans[i].a[cnt][j]=tmp[j];
                }
                cnt++;
            }while(next_permutation(tmp+1,tmp+9));
        }
    }
    while(t--){
        int n,k;
        scanf("%d%d",&n,&k);
        //if(n==9) printf("9 "),n--;
        if(n<9){
            for(int i=1;i<=n;i++){
                int pos =
                printf("%d",ans[n].a[ans[n].b[k].pos][i]);
                if(i==n) puts("");
                else printf(" ");
            }
        }
        else {
            printf("%d",n);
            for(int i=1;i<=n-9;i++) printf(" %d",i);
            for(int i=1;i<=8;i++) printf(" %d",ans[n].a[k][i]);
            puts("");
        }
    }
    return 0;
}

1006 string matching HDU 6629

题意:给你一个字符串,问从第1个字符(下标从0开始)到第n-1个字符开始求与原字符串求最长前缀时需要比较的次数

思路:这是一个exkmp模板题,exkmp可以求出一个字符串的每一个字符开始与另一个字符串的最长公共前缀长度,那么我们可以将他本身跟自己做exkmp算法,求出最长前缀长度,去掉第一位的前缀,就接近答案了,接近......那么还差哪些呢,我们求得是比较次数,相同的比较,ans++,那么不相等的也要比较,ans++,(那不是每个前缀长度+1就好了???当然不是)如果我的字符串都比到最后一位了,还相等,那这种情况跳出,就不需要去多判一次了,所以我们再累加的时候只需要判断前缀长度+当前位置-1是不是==字符串长度即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
char sa[1100000],sb[1100000];
int lena,lenb;
int p[1100000],ex[1100000];
//p数组是用来让B串自己匹配自己的
void exkmp()
{
    p[1]=lenb;
    int x=1;
    while(sb[x]==sb[x+1]&&x+1<=lenb) x++;//因为我们p[1]是具有一定性,所以我们不能直接用,所以要先暴力求出p[2]
    p[2]=x-1;
    int k=2;
    for(int i=3;i<=lenb;i++)
    {
        int pp=k+p[k]-1,L=p[i-k+1];//pp实际上是p
        if(i+L<pp+1) p[i]=L;//i-k+L<pp-k+1化简后i+L<pp
        else
        {
            int j=pp-i+1;
            if(j<0) j=0;
            while(sb[j+1]==sb[i+j]&&i+j<=lenb) j++;
            p[i]=j;
            k=i;
        }
    }
    x=1;
    while(sa[x]==sb[x]&&x<=lenb) x++;//ex[1]并不具有一定性,所以我们暴力求出ex[1]
    ex[1]=x-1;
    k=1;
    for(int i=2;i<=lena;i++)
    {
        int pp=k+ex[k]-1,L=p[i-k+1];
        if(i+L<pp+1) ex[i]=L;
        else
        {
            int j=pp-i+1;
            if(j<0) j=0;
            while(sb[j+1]==sa[i+j]&&i+j<=lena&&j<=lenb) j++;
            ex[i]=j;
            k=i;
        }
    }
}
int main()
{
    int t;scanf("%d",&t);
    while(t--){
        memset(p,0,sizeof p);
        memset(ex,0,sizeof ex);
        scanf("%s",sa+1);
        strcpy(sb+1,sa+1);
        lena=strlen(sa+1);lenb=strlen(sb+1);
        ll ans = 0;
        exkmp();
        for(int i=2;i<=lena;i++) {
            if(lena-i+1==ex[i]) ans+=1l*ex[i];
            else ans+=1l*(ex[i]+1);
        }
        printf("%lld\n",ans);
    }

    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值