【GDOI三校联考】积木游戏

15 篇文章 0 订阅
7 篇文章 0 订阅

Description:

Bessie的玩具箱里有N种(编号1至N)不同颜色的积木,第i种颜色的积木有a[i]个。每个积木就是一个1×1×1的正方体。刚开始,地板上没有任何的积木,Bessie每次从箱子里挑出一个积木(选择顺序由Bessie决定),然后有两种选择:

1、把这个积木直接放在地板上;

2、把这个积木叠放在之前已经放置好的某个积木正上方。

Bessie最终会把玩具箱里所有的积木全部拿出来,在地板上搭建积木塔。Bessie在搭建积木塔时还遵守一个规则:相同颜色的积木所处的水平高度必须相等。地板的水平高度是0,地板上方第一层的所有积木的水平高度都是1,地板上方第二层的所有积木的水平高度都是2,依次类推。

Bessie的朋友Andy来了,Andy是要把Bessie的积木塔通过N次操作后全部移走,Andy每次选择一种颜色k,然后把颜色是k的所有积木都从积木塔中移走,但是能进行这个操作的前提条件是:颜色是k的所有积木的上方都没有其他积木了。简单点说,Andy移走积木塔的整个过程就是这N种颜色的一个排列!对于Bessie的积木塔,Andy的不同移走方案,其实就是对应这N种颜色的不同的排列!

现在Bessie和Andy玩游戏,Bessie搭建好积木塔后,Andy就计算把积木塔移走的不同方案数(即上文提及的合法的N种颜色的不同排列的数量),不妨假设Andy算出的不同方案数是s, 如果s的范围在区间[minWays,maxWays]范围内,那么Bessie搭建的积木塔是“好塔”,否则就不是。然后Andy就把移走的积木塔打乱,重新放回Bessie的玩具箱里。然后他们又开始重复玩这个游戏,Bessie搭积木塔,Andy针对当前积木塔计算不同移走方案数,判断Bessie搭建的是不是“好塔”,然后把积木塔打乱,重新放回玩具箱….,他们不断重复这个游戏。

Bessie保证每次新搭建的积木塔和之前搭建的所有积木塔都是“本质不同”的!如果Bessie做不到这点,游戏就会结束!

下面定义“本质不同”:假设有积木塔A和积木塔B,只要存在不同的颜色i和颜色j,在积木塔A中至少有一个颜色i的积木“直接”铺在颜色j的积木的正上方,而在积木塔B中不存在颜色i的积木“直接”铺在颜色j的积木的正上方,那么积木塔A和积木塔B是“本质不同”的,否则A和B是本质相同的。“直接”的意思是指两个积木高度差距是1。

现在的问题是:Bessie能构造出多少个“本质不同”的“好塔”?答案模1000000007。

题解:

LJ搜索题。

如果把x颜色放y上面视作x到y连一条边。

则该图的可能数是 3n(n1)/2=315=14348907 3 n ∗ ( n − 1 ) / 2 = 3 15 = 14348907

发现不能有环,少了很多。

由于一个颜色的点的高度必须相同,又少了很多。

还剩6w+个图。

这个图就一定合法吗?不能。

分层跑最大流可以判合法。

方案数呢,暴力 6!=720 6 ! = 720 ,心态稳健。

Code:

#include<cstdio>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const int inf = 1 << 30;

int n, a[10], mi, mx, b[10][10], tb, du[10], d[10], dis[10];

const int M = 1000;

int final[M], next[M], to[M], r[M], tot = 1, S, T, co[M], dt[M], cur[M];
void cl() {
    fo(i, 1, T) final[i] = dt[i] = co[i] = cur[i] = 0;
    fo(i, 2, tot) next[i] = 0;
    tot = 1;
}
void link(int x, int y, int z) {
    next[++ tot] = final[x], to[tot] = y, r[tot] = z, final[x] = tot;
    next[++ tot] = final[y], to[tot] = x, r[tot] = 0, final[y] = tot;
}
int dfs(int x, int flow) {
    if(x == T) return flow;
    int use = 0;
    for(int i = cur[x]; i; i = next[i], cur[x] = i) {
        int y = to[i];
        if(r[i] && dt[y] + 1 == dt[x]) {
            int tmp = dfs(y, min(flow - use, r[i]));
            r[i] -= tmp, r[i ^ 1] += tmp, use += tmp;
            if(use == flow) return use;
        }
    }
    cur[x] = final[x];
    if(!(-- co[dt[x]])) dt[S] = T;
    ++ co[++ dt[x]];
    return use;
}

int p[10][10], q[10];
int bz[10], ans, sum;

void dd(int x) {
    if(x > n) {
        sum ++; return;
    }
    fo(i, 1, n) if(!bz[i]) {
        int ky = 1;
        fo(j, 1, b[i][0]) if(bz[b[i][j]])  {
            ky = 0; break;
        }
        if(ky) bz[i] = 1, dd(x + 1), bz[i] = 0;
    }
}

void dg(int x, int y) {
    if(tb > 9) return;
    if(x > n) {
        d[0] = 0; fo(i, 1, n) du[i] = 0;
        fo(i, 1, n) fo(j, 1, b[i][0]) du[b[i][j]] ++;
        fo(i, 1, n) if(!du[i]) d[++ d[0]] = i;
        fo(i, 1, d[0]) fo(j, 1, b[d[i]][0]) if(!(-- du[b[d[i]][j]])) d[++ d[0]] = b[d[i]][j];
        if(d[0] != n) return;
        int dep = 0;
        fd(i, d[0], 1) {
            if(!b[d[i]][0]) {dis[d[i]] = 1; continue;}
            int y = dis[b[d[i]][1]];
            fo(j, 2, b[d[i]][0]) if(y != dis[b[d[i]][j]]) return;
            dis[d[i]] = y + 1;
        }
        fo(i, 1, n) p[i][0] = q[i] = 0;
        fo(i, 1, n) p[dis[i]][++ p[dis[i]][0]] = i, dep = max(dep, dis[i]);

        fo(i, 2, dep) {
            cl();
            int tmp = 0;
            fo(j, 1, p[i][0]) {
                int x = p[i][j];
                tmp += a[x] - b[x][0];
                if(a[x] < b[x][0]) return;
                link(S, x, a[x] - b[x][0]);
                fo(k, 1, b[x][0]) link(x, b[x][k], inf), q[b[x][k]] ++;
            }
            fo(j, 1, p[i - 1][0]) {
                int x = p[i - 1][j];
                if(a[x] < q[x]) return;
                if(a[x] > q[x]) link(x, T, a[x] - q[x]);
            }

            co[0] = T; int fw = 0;
            for(; dt[S] < T;) fw += dfs(S, inf);
            if(fw < tmp) return;
        }
        sum = 0; dd(1);
        if(sum >= mi && sum <= mx) ans ++;
        return;
    }
    if(y > n) {
        dg(x + 1, x + 2);
        return;
    }
    tb ++;
    b[x][++ b[x][0]] = y;
    dg(x, y + 1); b[x][0] --;
    b[y][++ b[y][0]] = x;
    dg(x, y + 1); b[y][0] --;
    tb --;
    dg(x, y + 1);
}

int main() {
    scanf("%d", &n);
    fo(i, 1, n) scanf("%d", &a[i]);
    scanf("%d %d", &mi, &mx);
    S = n + 1; T = n + 2;
    dg(1, 2);
    printf("%d\n", ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值