[数位DP][状压DP]密码

题目描述

在又一次消灭林登·万的战斗中,指挥官moreD缴获了一个神奇的盒子。盒子异常的坚固,以至于完全无法摧毁,唯一打开的方式是通过盒上的密码锁。
经过仔细的调查,研究人员一致认为这个盒子中隐藏了林登·万和他的弟弟林登·图的秘密。然而moreD使用了许多办法,都没能打开这个盒子。最后只好将这个盒子封存在了仓库的底层。
事情并没有结束。moreD之所以没能打开这个盒子,是因为老牌的调查员/邪教徒LCJ隐瞒了它的调查结果。LCJ经过不懈的努力,得出了结论。即:给你一个长度不超过17的由0~9组成的无前导0的字符串S,S中的数字排列组成的无前导零的能被17整除的整数中字典序第K小的那个数就是密码。
尽管解开了密码,然而处于对未知的恐惧,LCJ最终并没有打开盒子。然而另一个资历较浅的调查员/邪教徒,你,YDMan不知通过什么办法得知了上述信息,并得到了S和K。现在你决定要解开这个密码,来取得“终极的智慧”。

Input
一行,一个由0~9组成的字符串S和一个不超过17!的正整数K。

Output
一行,即密码。数据保证有解。

Sample Input
输入1:
17 1

输入2:
2242223 2

Sample Output
输出1:
17

输出2:
2242232

Data Constraint
对于40%的数据,字符串S长度<=12,K<=2*10^6。
对于100%的数据,字符串S长度<=17,K<=17!。

后记:

事实上,盒子中装的正是伦道夫·卡特当年穿越银匙之门的银色钥匙。YDMan在“掌(bei)握(xia)智(huai)慧(le)”智慧,终于成为了一个伟大的“诗(huan)人(zhe)”。而林登·万则和和他的弟弟继续使用林登·图钥匙与指挥官moreD进行不屈不挠的斗争。

分析

其实第一眼看状压第二眼看数位(可我怎么没想到结合起来呢?)
我们因为要字典序,所以给数位小到大排序
然后我们用二进制状压表示选或不选该数位,另一个表示结合产生的余数为0~16的方案数
然后我们只要枚举状态从满状态中减去即可,最后输出没被选上的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <memory.h>
#include <algorithm>
#define rep(i,a,b) for (i=a;i<=b;i++)
typedef long long ll;
const ll MOD=17;
using namespace std;
int a[18];
ll fact[18],k,
pow[18]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072},
p10[18],f[1<<17][17];
int len,n;
char s[20];
int main() {
    int i,j,l;
    scanf("%s",&s);
    scanf("%lld",&k);
    len=strlen(s)-1;
    rep(i,0,len) a[i]=s[i]-'0';
    sort(a,a+len+1);
    n=(1<<len+1)-1;
    f[0][0]=1;
    rep(i,0,n-1) {
        int w=1;
        rep(j,0,len)
        if (i&pow[j]) w=w*10%MOD;
        rep(j,0,len)
        rep(l,0,16)
        if (!(i&pow[j])) f[i|pow[j]][(l+a[j]*w%MOD)%MOD]+=f[i][l];
    }
    fact[0]=1;
    rep(i,1,17) fact[i]=fact[i-1]*i;
    rep(i,0,n-1) {
        int c[10];
        memset(c,0,sizeof(c));
        rep(j,0,len) if (i&pow[j]) c[a[j]]++;
        rep(j,0,9) rep(l,0,16) f[i][l]/=fact[c[j]];
    }
    int s=n,p=0;
    p10[0]=1;
    rep(i,1,17) p10[i]=p10[i-1]*10%MOD;
    rep(i,0,len) {
        int last=-1;
        rep(j,0,len)
        if (s&pow[j]) {
            if (a[j]==last) continue;
            last=a[j];
            if (!i&&!a[j]) continue;
            if (f[s^pow[j]][(17-(p+a[j]*p10[len-i])%MOD)%MOD]>=k) {
                s^=pow[j];
                p=(p+a[j]*p10[len-i])%MOD;
                printf("%d",a[j]);
                break;
            }
            else k-=f[s^pow[j]][(17-(p+a[j]*p10[len-i])%MOD)%MOD];
        }
    }
    rep(i,0,len)
    if (s&pow[i]) printf("%d",a[i]);
}

猜你喜欢

转载自blog.csdn.net/ssl_qyh0ice/article/details/81104827
今日推荐