[Baltic2014]sequence 解题报告

想了很久还是不会。。然后开始各种乱搞全都不行。。
最后看了题解感觉好厉害!

首先我们将问题放缩。设x的每一位的数字集合是S(x),则对于给定数列 {Bi}(i[0,k)) , Bi{0,1,2,3,4,5,6,7,8,9},i[0,k) ,要求 BiS(n+i) ,求最小的n。
这样的话,如果我们枚举n的个位,就会将问题转化为10个 k10 规模的问题!
当k=1时,显然就可以直接贪心了。
所以时间复杂度就是 O(10klog10k) 的。

但是还有不少非常蛋碎的细节。。
当k=2的时候,我们可能会选择让个位=9,但是这样的话k的规模就不会缩小了,不过显然我们连续枚举两个9是毫无卵用的。所以这里我们需要特判。
还有前缀0,如果前缀0中有一个0存在是在它还没有发生任何+1的时候产生了贡献,那么我们就需要在最前面补1;否则的话就不需要。
贪心的时候有0的话也很麻烦,因为如果只有它自己,就应该在前面加10,否则的话就应该加在最前面那个数的后面。

我在乱搞的时候以为只有当我选这一位会产生贡献的时候才选这一位,然而其实并不是。
比如说
2
2 2
答案应该是20,个位的0并未产生任何贡献。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstdlib>
#include<cmath>
const int K=1e5+5,N=1e6+5;
typedef long long LL;
int pos[10][K];
bool exist[N][10];
int f[N][10];
int k;
int check(int n,int x,int cur,int Mod){
    //if(n==8)printf("check(%d,%d)\n",n,x);
    for(int i=cur;i;--i){
        /*if(n==8){
            printf("%d:%d\n",n+pos[x][i],exist[n+pos[x][i]][x]);
        }*/
        if(x?!exist[(n+pos[x][i])%Mod][x]:!(exist[(n+pos[x][i])%Mod][0]||(n+pos[x][i])%Mod<Mod/10))
            return i;
    }
    //if(n==102451)printf("check(%d,%d)=%d\n",n,x,1);
    return 0;
}
int main(){
    freopen("bzoj_3917.in","r",stdin);
    //freopen("bzoj_3917_test.out","w",stdout);
    int x;

    exist[0][0]=1;
    for(int i=1;i<N;++i)
        for(x=i;x;x/=10)
            exist[i][x%10]=1;

    /*for(int i=1;i<=100;++i){
        printf("%d:",i);
        for(int j=0;j<10;++j)printf("%d",exist[i][j]);
        puts("");
    }*/

    scanf("%d",&k);
    int b;
    for(int i=0;i<k;++i){
        scanf("%d",&b);
        pos[b][++pos[b][0]]=i;
    }

    for(int i=10;i--;)random_shuffle(pos[i]+1,pos[i]+pos[i][0]+1);

    LL ans=1e18;
    LL prod;
    LL now;
    LL power=1;


    for(int n=0;n<10;++n){
        //printf("---%d---\n",n);
        if(n==power*10)power*=10;
        prod=1e6;
        now=n;
        for(int i=10;i--;)f[n][i]=pos[i][0];
        for(int i=9;now<ans&&i;--i)
            if(f[n][i]=check(n,i,pos[i][0],10)){
                now+=prod*i;
                prod*=10;
            }
        if(now<ans)
            if(f[n][0]=check(n,0,pos[0][0],10)){
                if(prod>1e6){
                    x=now/(prod/10);
                    now-=x*(prod/10);

                    now+=x*prod;
                }
                else now+=prod*10;
            }
        if(now)ans=min(ans,now);

        /*if(n==8)
            for(int i=0;i<10;++i)
                printf("f(%d)=%d\n",i,f[n][i]?pos[i][f[n][i]]:0);*/
    }


    for(int n=10;n<101;++n){
        //printf("---%d---\n",n);
        if(n==power*10)power*=10;
        prod=1e6;
        now=n;
        for(int i=10;i--;)f[n][i]=f[n%power][i];
        for(int i=9;now<ans&&i;--i)
            if(f[n][i]=check(n,i,f[n%power][i],power*10)){
                now+=prod*i;
                prod*=10;
            }
        if(now<ans)
            if(f[n][0]=check(n,0,f[n%power][0],power*10)){
                if(prod>1e6){
                    x=now/(prod/10);
                    now-=x*(prod/10);

                    now+=x*prod;
                }
                else now+=prod*10;
            }
        if(now)ans=min(ans,now);

        //if(n==90)cout<<now<<endl;

        /*if(n==8)
            for(int i=0;i<10;++i)
                printf("f(%d)=%d\n",i,f[n][i]?pos[i][f[n][i]]:0);*/
    }
    cout<<ans<<endl;
    //cout<<tot<<endl;
}

总结:
①有时将原问题松弛也往往会有奇效。
②特殊情况一定要想清楚。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值