五一集训:day3A

题目:同时抛掷n个理想的正方体骰子,有多少概率使得其中若干个骰子点数和为m?

多组数据

对于 50% 的数据,n2

对于 100% 的数据,1n8,1T10000,1m50

挺好的题,在这么多天的集训里算一道清流了

先想暴力:枚举每个骰子的点数,最后再跑一遍dfs判断

O(6^n*2^n)死的很惨

优化:枚举每个骰子点数,最后用完全背包判断

O(6^n*36n)死的还行

再优化:由于每个骰子对背包的影响是独立的,所以可以在枚举的时候顺带维护背包

O(6^n*6n),能过,但不是最好

注意到优化中,每一个背包状态都只有0/1,即能否组成,而f至多50个

所以可将f压到一个long long里,状压dp即可

O(6^n)

顺带,这个题数据范围很大,但是有很多重复,所以应该记录哪些n已经被询问,这样避免T

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath> 
#include<iostream>
using namespace std;
#define O(x) cout << #x << " " << x << endl;
#define B cout << "breakpoint" << endl;
inline int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        (ans *= 10) += ch - '0';
        ch  = getchar();
    }
    return ans * op;
}
#define clr(a) memset(a,0,sizeof(a));
int ans[10][50];
bool f[50];
int a[50];
int n,m;
void dfs(int cur)//cur:第几个 
{
    if(cur == n + 1)
    {
        for(int j = 1;j <= 48;j++)
            ans[n][j] += f[j];
        return;
    }
    for(int i = 1;i <= 6;i++)//cur的值是啥 
    {
        a[cur] = i;
        vector<int> tp;
        for(int j = 6 * n;j >= a[cur];j--) 
        {
            if(f[j] == 0 && f[j - a[cur]]) tp.push_back(j);
            f[j] |= f[j - a[cur]];
        }
        dfs(cur + 1);
        for(int j = 0;j < tp.size();j++) f[tp[j]] = 0;
    }
}
int gcd(int x,int y) { return !y ? x : gcd(y,x % y); }
bool exist[10];
int main()
{
    int t = read();
    while(t--)
    {
    f[0] = 1;
    n = read(),m = read();
    if(m > 48) {printf("0/1
"); continue; }
    if(!exist[n]) exist[n] = 1,dfs(1);
    int base = pow(6,n);
    int g = gcd(ans[n][m],base);
    printf("%d/%d
",ans[n][m] / g,base / g);
    }
}
View Code
原文地址:https://www.cnblogs.com/LM-LBG/p/10845692.html