AtCoder Beginner Contest 309

F - Box in Box

题意:

给定 n n n个长宽高为 w , h , d w, h, d w,h,d的长方体,问是否存在两个长方体 i , j i,j i,j满足 w i < w j , h i < h j , d i < d j w_i<w_j,h_i<h_j,d_i<d_j wi<wj,hi<hj,di<dj

思路:

因为我们只关心相对大小,所以我们先对其进行离散化,然后按 w w w从小到大排序。枚举到当前长方体时,将离散化后的 h h h作为下标,对应的值为 d d d,那么我们只需查询小于当前 h h h的所以下标对应的值的最小值即可。

代码:

const int N = 6e5 + 50; 
const int M = N << 1;
const int mod = 1e9 + 7;

template<typename T>
struct BIT{
    const int n;
    vector<T> t;

    BIT<T> () {}
    BIT<T> (int _n): n(_n), t(_n + 1, inf) { }
    BIT<T> (int _n, T *a): n(_n) {
        memset(t, 0, sizeof(t));
        for (int i = 1; i <= n; ++ i){
            t[i] += a[i];
            int j = i + lowbit(i);
            if (j <= n) t[j] += t[i];
        }
    }
    void add(int i, T x){ 
        while (i <= n){
            t[i] += x;
            i += lowbit(i);
        }
    }
    void add(int i, int j, T x) {
        add(i, x); add(j + 1, -x);
    }
    void updata(int i, int val) {
        while(i <= n) {
            t[i] = min(t[i], val);
            i += lowbit(i);
        }
    }
    T get(int i) {
        int res = inf;
        while(i) {
            res = min(res, t[i]);
            i -= lowbit(i);
        }
        return res;
    }
};


int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

    int n;
    cin >> n;
    vector<pair<int, pii>> a(n);
    vector<int> lsh;
    lsh.pb(0);
    for(int i = 0; i < n; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        vector<int> now = {x, y, z};
        sort(all(now));
        a[i] = make_pair(now[0], make_pair(now[1], now[2]));
        lsh.pb(now[1]), lsh.pb(now[2]);
    }
    sort(all(lsh));
    lsh.erase(unique(all(lsh)), lsh.end());
    sort(all(a));
    BIT<int> bit(n * 2 + 10);
    vector<int> id(n + 1);
    int last = 0;
    for(int i = 0; i < n; i++) {
        id[i] = lower_bound(all(lsh), a[i].se.fi) - lsh.begin();
        while(last < i && a[last].fi < a[i].fi) {
            bit.updata(id[last], a[last].se.se);
            last++;
        }
        if(bit.get(id[i] - 1) < a[i].se.se) {
            cout << "Yes\n";
            return 0;
        }
    }
    cout << "No\n";

	return 0;
}

G - Ban Permutation

题意:

构造长度为 n ( n ≤ 100 ) n(n\leq100) n(n100)的排列,满足 ∣ p i − i ∣ ≥ x ( x ≤ 5 ) |p_i-i| \geq x(x\leq5) piix(x5),输出合法的方案数。

思路:

如果反过来思考求 ∣ p i − i ∣ < x |p_i-i| < x pii<x的数量,那么 p i p_i pi的取值最多也就9种。看起来会更好做一点?所以考虑容斥,求出满足至少有一个位置满足 ∣ p i − i ∣ < x |p_i-i|<x pii<x的方案数。
我们定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]:前i个位置填了 j j j个数, j j j个数均满足 ∣ p i − i ∣ < x |p_i-i|<x pii<x,且第 i i i个位置选的数的状态是 k k k的总方案数。 k k k是一个长度为 2 ∗ x − 1 2*x-1 2x1的二进制数。
转移很好转移,最后利用容斥转一下就可以了。

代码:

int dp[105][105][600];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

    int n, x;
    cin >> n >> x;
    int lim = 1 << (2 * x - 1);
    dp[0][0][0] = 1;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j <= i; j++) {
            for(int k = 0; k < lim; k++) {
                if(!dp[i][j][k]) continue;
                dp[i + 1][j][k >> 1] = (dp[i + 1][j][k >> 1] + dp[i][j][k]) % mod;
                for(int l = i + 1 - x + 1; l <= i + 1 + x - 1; l++) {
                    if(l >= 1 && l <= n && !((k >> 1) & (1 << (l - i - 2 + x)))) {
                        dp[i + 1][j + 1][((k >> 1) | (1 << (l - i + x - 2)))] += dp[i][j][k];
                        dp[i + 1][j + 1][((k >> 1) | (1 << (l - i + x - 2)))] %= mod;
                    }
                }
            }
        }
    }
    LL fact[111];
    fact[0] = 1;
    for(int i = 1; i <= 100; i++) {
        fact[i] = fact[i - 1] * i % mod;
    }
    LL ans = 0;
    for(int i = 0; i <= n; i++) {
        LL cur = 0;
        for(int j = 0; j < lim; j++) { 
            cur = (cur + dp[n][i][j]) % mod;
        }
        cur = cur * fact[n - i] % mod;
        if(i & 1) ans = (ans - cur + mod) % mod;
        else ans = (ans + cur) % mod;
    }
    cout << ans << '\n';

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值