想了很久还是不会。。然后开始各种乱搞全都不行。。
最后看了题解感觉好厉害!
首先我们将问题放缩。设x的每一位的数字集合是S(x),则对于给定数列{Bi}(i∈[0,k)),Bi∈{0,1,2,3,4,5,6,7,8,9},?i∈[0,k),要求Bi∈S(n+i),求最小的n。
这样的话,如果我们枚举n的个位,就会将问题转化为10个k10规模的问题!
当k=1时,显然就可以直接贪心了。
所以时间复杂度就是O(10?klog10k)的。
但是还有不少非常蛋碎的细节。。
当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;
}
总结:
①有时将原问题松弛也往往会有奇效。
②特殊情况一定要想清楚。
时间: 2024-10-11 06:16:57