TC SRM567 DIV2 T3 MountainsEasy

4 篇文章 0 订阅
3 篇文章 0 订阅

前言

TC百题计划,走起!

题目描述

今有一图青山绿水,山神安山 N N 座。重峦叠嶂,峰峰起伏如此:

For 0 <= i < N:
    For 0 <= x < W:
        For 0 <= y <= Y[i] - |x - X[i]|:
            pix[x, y] := 'X'

即是:

..X...
.XXXX.
XXXXXX

如是,山峰坐标多变莫测。今予你山水画一幅,请求山峰坐标的不同序列,答案对109+9取模。

Example

{“X.”,
“XX”}
2
Returns: 5
Here one of the mountains is completely covered by the other. The five possible sequences are:
(0, 1), (0, 1)
(0, 1), (0, 0)
(0, 1), (1, 0)
(0, 0), (0, 1)
(1, 0), (0, 1)

数据范围

  • 画布长宽皆至五十
  • 1N50 1 ≤ N ≤ 50

分析

PART 1 找山峰

通过给出的图,我们可以先确定有一些地方是一定是有山峰的,假设为 peaks p e a k s 个位置,那么如何去算这个 peaks p e a k s 呢?
容易发现,一个点如果是山峰,当且仅当这个点是这一列最高点并且两边的最高点都不高于它。
这样就可以用非常简单的方法先求出 peaks p e a k s

PART 2 转化问题

我们设一个共有 tot t o t 个格子是山,总共有 N0 N 0 座山,还有 N N 座山山峰的位置不确定(N即是 N0peaks N 0 − p e a k s ,注意这里的记法与题目给出的有所不同)。那么问题就转化成了我们给这 tot t o t 个格子从 1 1 ~tot编号,有 N0 N 0 个相同的物品,一个格子可以放多个物品。有 peaks p e a k s 个格子至少要放一个。放完后,我们把每个物品放到盒子的编号提出来变成一个序列,求这个序列的种数。
刚刚应该注意到非常重要也是非常关键的一点,我们结合这个样例,因为盒子的序号顺序有差异的序列也算是不同的,因此,事实上可以把这 N0 N 0 个物品看作是不相同的,然后求它们放在盒子里的方案数。
问题已经到了这一步了,就可以开始思考容斥的做法了。

PART 3 容斥

按照套路,我们设 g(x) g ( x ) 为恰好有 x x 个有限定的盒子没有满足限定(即这x个盒子里没放一个物品)。
那么要求的答案就是 totN0g(1)g(2)...g(picks) t o t N 0 − g ( 1 ) − g ( 2 ) − . . . − g ( p i c k s ) ,即,总共的方案数是 N0 N 0 个物品,每个物品有都有 tot t o t 个盒子可以放,再减去恰好是 1 1 个盒子不满足条件,2个盒子不满足条件,…, picks p i c k s 个盒子不满足条件的方案数。
当然 g(x) g ( x ) 不好求,那么我们可以设一个 f(x) f ( x ) 表示至少有 x x 个有限定的盒子没有满足限定。那么容易得出f(x)=(picksx)×(totx)N0,即,先选出 x x 个盒子不放任何东西,这样还剩下totx个盒子,也就是这 N0 N 0 个物品的选择。
再来看一看 f(x) f ( x ) g(x) g ( x ) 的关系: f(x)=picksk=x(kx)g(k) f ( x ) = ∑ k = x p i c k s ( k x ) g ( k ) ,即,对于恰好 k k 个限定盒不满足的情况,这k个位置的任意 x x 组合都会在f(x)中被计算 g(k) g ( k ) 次。
这样答案就可以这样表示: totN0f(1)+f(2)f(3)+...+(1)picksf(picks) t o t N 0 − f ( 1 ) + f ( 2 ) − f ( 3 ) + . . . + ( − 1 ) p i c k s f ( p i c k s ) ,即:

i=0peaks(peaksi)×(1)i×(toti)N0 ∑ i = 0 p e a k s ( p e a k s i ) × ( − 1 ) i × ( t o t − i ) N 0

(感谢 cly_none大佬指教)

参考程序

//tc is healthy, just do it
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1000000009;
const int ArSize = 55;
int fac[ArSize], inv[ArSize];

class MountainsEasy {
private:
    int H, W;
    void init() {   /*预处理出50以内的阶乘及其逆元,后面算组合数会用到*/
        int i;
        for (fac[0] = i = 1; i <= 50; i++) fac[i] = (LL)i * fac[i - 1] % MOD;
        inv[50] = fast_pow(fac[50], MOD - 2);
        for (i = 49; i >= 0; i--) inv[i] = (LL)(i + 1) * inv[i + 1] % MOD;
    }
    int fast_pow(int bs, int ex) {  // 快速幂
        int res = 1;
        for (; ex > 0; ex >>= 1, bs = (LL)bs * bs % MOD) if (ex & 1) res = (LL)res * bs % MOD;
        return res;
    }
    int C(int n, int r) {   // 组合数
        return (LL)fac[n] * inv[r] % MOD * inv[n - r] % MOD;
    }
public:
    int countPlacements( vector <string> picture, int N );
};

int MountainsEasy::countPlacements(vector <string> picture, int N) {
    H = picture.size(), W = picture[0].length();
    int x, y, N0 = N;
    for (x = 0; x < W && picture[H - 1][x] == '.'; x++);
    if (x == W) return 0;
    else {
        int tot = 0;
        // 找确定的山峰个数
        for (; x < W; x++) {
            for (y = 0; y < H && picture[y][x] == '.'; y++);
            if (y == H) continue;
            if (!y || 
                (!x && (x + 1 == W || picture[y - 1][x + 1] == '.')) ||
                 (x + 1 == W && (!x || picture[y - 1][x - 1] == '.')) || 
                 (x && x + 1 < W && picture[y - 1][x - 1] == '.' && picture[y - 1][x + 1] == '.')
            )
                --N;
            tot += H - y;
        }
        if (N < 0) return 0;
        init();
        int peaks = N0 - N;
        LL res = 0;
        // 容斥部分,极其精简吧
        for (int i = 0; i <= peaks; i++)
            if (i & 1) res = (res + MOD - (LL)C(peaks, i) * fast_pow(tot - i, N0) % MOD) % MOD;
            else res = (res + (LL)C(peaks, i) * fast_pow(tot - i, N0) % MOD) % MOD;
        return (int)res;
    }
}

总结

首先这个原题的英文题面废话很多,很长,转化之后就很简单的一个意思。不过转化到物品不同这一性质其实不容易,因为之前抽象出来的问题描述的不是很好…(我的锅…)但是cly大佬还是一眼就看出来了,个人认为自己的抽象能力还有待提升。解决了计数,找山峰其实也是很一个很毒瘤的模拟题,一开始写的很麻烦,改来改去改了三次才想到这个简单的思路。总而言之,这题考验的还是你的抽象能力,是否能发现问题最本质的性质,无论是在求山峰还是算方案的时候都是很需要这种能力的。我认为这是道好题!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值