UVALive 7344 I - Numbered Cards

来源:互联网 发布:双胞胎 双飞 知乎 编辑:程序博客网 时间:2024/04/29 17:16

题目描述:

You have N cards and each has an unique number between 1 and N written on it. In how many
ways can you select a non-empty subset of the cards such that the number written on any two of your
selected cards don’t have any common digits?
For example, when N = 12, {1, 2, 3}, {2, 11}, {3, 4, 5, 6, 7, 8, 9, 12} are some valid selections. But
{1, 2, 10}, {2, 5, 12} are not allowed.
Input
The first line of the input contains an integer T (T ≤ 15) which is the number of test cases. Each of
the following T lines denote a test case, containing an integer N (1 ≤ N < 109
).
Output
For each test case, output the case number followed by the number of subsets modulo 1000000007.

题解:

非常好的一道题目. 首先想到dp来表示之前已经用了几个数, 然后转移的时候用到了偏序(因为是集合的原因),我们强制让最后一个数包括最小的那个. 然后转移的g[2^i]是用3(4)进制状压的数位dp做.

重点:

1.dp的想法来做,状压
2.集合无序性,我们用偏序做.
3.二进制都是恰好一定要用掉这么多,不能采用用不超过这个范围(但没必要全用完)这样的思维,因为会有很多重复情况. ——这是赛场上差点犯的错误QAQ
4.用3进制状压:不能用,要用还没用,要用已经用过了至少一次

代码:

#include <iostream>#include <cstdio>#include <cstring>using namespace std;typedef long long ll;const int maxn = 1024 + 100;const int M = 1e9 + 7.1;int fac[20], n, g[maxn], f[maxn], d[20][1024 * 1024 + 100];int  s[20];int add(int a, int b) {    int c = a + b;    if(c >= M)        c -= M;    return c;}int sub(int a, int b) {    if(a < b)        a += M;    return a - b;}int dfs(int pos, int limit, int sta, int first) {    //printf("222    %d\n", sta);     if(pos < 0) {        if(first)            return 0;        int flag = 1;        for(int i = 0; i < 10; i++) {            if((1 << (2 * i + 1)) & sta) {                flag = 0;                break;            }        }        return flag;    }    if(limit == 0 && first == 0 && d[pos][sta] != -1)        return d[pos][sta];    int ans = 0;    for(int i = 0; i < 10; i++) {        if(((1 << (2 * i) ) & sta) == 0 && !(i == 0 && first))            continue;        if(first && i == 0) {            ans = add(ans, dfs(pos - 1, 0, sta, 1));            continue;        }        int tmp = sta;        if((1 << (2 * i + 1)) & sta)            tmp = sta ^ (1 << (2 * i + 1));        if(limit) {            if(i > s[pos])                continue;            if(i == s[pos]) {                ans = add(ans, dfs(pos - 1, 1, tmp, 0));            }            else {                ans = add(ans, dfs(pos - 1, 0, tmp, 0));            }        }        else {            ans = add(ans, dfs(pos - 1, 0, tmp, 0));        }    }    if(!limit && !first)        d[pos][sta] = ans;    return ans;}void solve() {    int sn = 0, tmp = n;    while(tmp != 0) {        s[sn] = tmp % 10;        tmp /= 10;        sn++;    }    // printf("333 %d\n", sn);    g[0] = 0;    g[1] = 0;    for(int sta = 2; sta < (1 << 10); sta++) {        int num = 0;        for(int i = 0; i < 10; i++) {            if((1 << i) & sta)                num = num + (1<<(2 * i)) + (1 << (2 * i + 1));        }        g[sta] = dfs(sn - 1, 1, num, 1);        // if(sta <= 8) printf("111  %d   %d\n", sta, g[sta]);        // if(g[sta] != 0) printf("111 %d  %d\n", sta, g[sta]);    }    memset(f, 0, sizeof(f));    f[0] = 1;    for(int sta = 1; sta < (1 << 10); sta++) {        int number;        for(int k = 0; k < 10; k++) {            if((1 << k) & sta) {                number = k;                break;            }        }        for(int ss = ((sta - 1) & sta);ss != sta; ss = ((ss - 1) & sta)) {            if((1 << number) & (sta - ss))                f[sta] = add(f[sta], (ll)f[ss] * (ll)g[sta - ss] % M);        }        // if(sta <= 8) printf("222 %d  %d\n", sta, f[sta]);    }    int ans = 0;    for(int sta = 1; sta < (1 << 10); sta++) {        ans = add(ans, f[sta]);    }    printf("%d\n", ans);}int main() {    //freopen("i.txt", "r", stdin);    memset(d, -1, sizeof(d));    int ncase;    scanf("%d", &ncase);    for(int _ = 1; _ <= ncase; _++) {        // memset(d, -1, sizeof(d));        printf("Case %d: ", _);        scanf("%d", &n);        solve();    }    return 0;}
0 0
原创粉丝点击