zoukankan      html  css  js  c++  java
  • 【10.15校内测试】【组合数学】【单调栈】【回滚莫队】

    Solution

    根据题意可知,第$i$列放置的个数和第$i+kn$列是一样的。

    再由题目中$m=n$的数据找到灵感,可以用dp完成。

    $m=n$时,$dp[i][j]$表示第$i$列前一共放置$j$个的方案数,那么$dp[i][j]=sum{dp[i-1][t]*C(n,t)}$。最后答案就是$dp[n][c]$。

    而我们发现,在$n$以内的第$i$列后面一共有$(m-i)/n+1$列和它一样(包括它自己)

    所以在上面转移方程基础上变成$dp[i][j]=sum{dp[i-1][t]*C(n,t)^{(m-i)/n+1}}$就行了。

    预处理$C(n,t)^{(m-i)/n+1}$就可以过了。

    Code

    #include<bits/stdc++.h>
    #define LL long long
    #define mod 1000000007
    using namespace std;
    
    int n, c;
    LL dp[105][10005], ini[105][10005];
    LL fac[105], inv[105], m;
    
    LL mpow(LL a, LL b) {
        LL ans = 1;
        for(; b; b >>= 1, a = a * a % mod)
            if(b & 1)    ans = ans * a % mod;
        return ans;
    }
    
    LL C(int p, int q) {
        if(p < q)    return 0;
        if(p == q || q == 0)    return 1;
        return fac[p] * inv[q] % mod * inv[p - q] % mod;
    }
    
    void init() {
        fac[0] = 1;
        for(int i = 1; i <= 100; i ++)
            fac[i] = fac[i - 1] * i % mod;
        inv[100] = mpow(fac[100], mod - 2); inv[0] = 1;
        for(int i = 99; i >= 1; i --)
            inv[i] = inv[i + 1] * (i + 1) % mod;
        for(int i = 1; i <= n; i ++) {
            LL p = (m - i) / n + 1;
            for(int t = 0; t <= n; t ++)
                ini[i][t] = mpow(C(n, t), p);
        }
    }
    
    int main() {
        freopen("chess.in", "r", stdin);
        freopen("chess.out", "w", stdout);
        scanf("%d%lld%d", &n, &m, &c);
        init();
        dp[0][0] = 1;
        for(int i = 1; i <= n; i ++)
            for(int j = 0; j <= min(c, n * i); j ++)
                for(int t = 0; t <= min(j, n); t ++)
                    dp[i][j] = (dp[i][j] + dp[i-1][j-t] * ini[i][t] % mod) % mod;
        printf("%lld", dp[n][c]);
        return 0;
    }

    Solution

    单调栈。由于数据随机给出,我们考虑对暴力进行优化。

    显然对于一个右端点$r$,如果我们暴力地从$r$ 开始向左枚举左端点$l$,只要某一个l 满足$A[l] > A[r]$,则$l$ 不必再继续向左枚举。

    考虑到上面的性质,我们从右到左枚举右端点,并使用一个单调递减的栈对右端点进行维护。

    令当前枚举到$i$,并且将向栈中加入该位置。为了维护栈的单调性可能需要弹栈(当$A[i] > A[stk[top]]$时弹栈),在弹栈时,以弹出的位置为右端点$r$,左端点在$i + 1$ 到$r$ 中进行选取。

    但具体来讲,左端点如何快速选取呢?
    我们考虑对于栈中存储的位置$stk[t]$,$pos[t]$ 记录$stk[t]$(含)到$stk[t-1]$(不含)的区间中最靠左的能够作为左端点的位置(即该位置到$stk[t-1]$中所有数均不比该位置小,且该位置最靠左); $minn[t]$ 记录该左端点的值。在弹栈的过程中不断更新当前选择的左端点,即若$minn[top]$ 小于当前左端点的值,则更新。

    当前左端点为$pos[top]$。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    
    inline void read(int &x) {
        x = 0; char ch = getchar();
        while(ch > '9' || ch < '0')    ch = getchar();
        while(ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
    }
    
    int stk[10000005], pos[10000005], minn[10000005], a[10000005];
    int ans, n;
    void work() {
        int tp = 0;
        int ans = 1;
        stk[++tp] = n; pos[tp] = n; minn[tp] = a[n];
        for(int i = n - 1; i >= 1; i --) {
            int tmin = a[i+1], tpos = i + 1;
            while(tp && a[stk[tp]] < a[i]) {
                ans = max(ans, stk[tp] - tpos + 1);
                if(minn[tp] < tmin) {
                    tmin = minn[tp];
                    tpos = pos[tp];
                }
                tp --;
            }
            if(!tp) {
                stk[++tp] = i; pos[tp] = i; minn[tp] = a[i];
                continue;
            }
            if(a[i] < tmin) {
                tmin = a[i];
                tpos = i;
            }
            stk[++tp] = i, pos[tp] = tpos, minn[tp] = tmin;
        }
        printf("%d", ans);
    }
    
    int main() {
        freopen("array.in", "r", stdin);
        freopen("array.out", "w", stdout);
        read(n);
        n ++;
        a[1] = (1 << 30);
        for(int i = 2; i <= n; i ++)    read(a[i]);
        work();
        return 0;
    }

    Solution

    首先就想到莫队,可是加入操作好维护,删除非常麻烦。

    然后就学习到了一个很精髓的莫队!回滚莫队!人如其名,滚滚滚滚滚就好了....

    具体怎么搞?每次将左端点在一个块中的分成一组,然后按右端点排序,这样保证右端点一直往后扩展就好了,左端点在一个块内滚....

    也就是每次同组的询问从这个块的下一个块左端点重新开始往左更新,每次更新答案后都是从头开始。这样免去了删除操作。

    然后对于这道题的合并,开一个双向链表维护就可以叻。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    
    int bl[100005], a[100005];
    
    struct QAQ {
        int l, r, id;
    } qus[100005], que[100005];
    bool cmp_l(QAQ a, QAQ b) { return a.l < b.l; }
    bool cmp_r(QAQ a, QAQ b) { if(a.r == b.r)    return a.l < b.l; return a.r < b.r; }
    
    int tag[100005], vis[100005], idc, l[100005], r[100005];
    void update(int x) {
        if(tag[x] != idc)    tag[x] = idc, vis[x] = 0, l[x] = x, r[x] = x;
    }
    
    int stk[100005], tp, n, m;
    void delet() {
        while(tp) {
            int x = stk[tp --];
            if(x != 1 && vis[x - 1])    r[l[x - 1]] = x - 1;
            if(x != n && vis[x + 1])    l[r[x + 1]] = x + 1;
            vis[x] = 0;
        }
    }
    
    int now_ans;
    void get(int x, int opt) {
        update(x); update(x - 1); update(x + 1);
        int L = x, R = x;
        if(x != 1 && vis[x - 1])    L = l[x - 1];
        if(x != n && vis[x + 1])    R = r[x + 1];
        now_ans = max(now_ans, R - L + 1);
        r[L] = R; l[R] = L; vis[x] = 1;
        if(opt)    stk[++tp] = x;
    }
    
    int bsize, num, ans[100005];
    void work() {
        sort(qus + 1, qus + 1 + m, cmp_l);
        int now = 1;
        for(int i = 1; i <= n; i += bsize) {
            now_ans = 1, num = 0;
            while(now <= m && qus[now].l >= i && qus[now].l <= min(n, i + bsize - 1))    que[++num] = qus[now], now ++;
            if(!num)    continue;
            idc ++;
            sort(que + 1, que + 1 + num, cmp_r);
            int R = min(n, i + bsize - 1);
            for(int j = 1; j <= num; j ++) {
                while(R < que[j].r) {
                    int t = a[++R];
                    get(t, 0);
                }
                int tmp = now_ans;
                for(int k = min(que[j].r, min(n, i + bsize - 1)); k >= que[j].l; k --) {
                    int t = a[k];
                    get(t, 1);
                }
                ans[que[j].id] = now_ans; delet();
                now_ans = tmp;
            }
        }
        for(int i = 1; i <= m; i ++)    printf("%d
    ", ans[i]);
    }
    
    int main() {
        freopen("ants.in", "r", stdin);
        freopen("ants.out", "w", stdout);
        scanf("%d%d", &n, &m);
        bsize = sqrt(n);
        for(int i = 1; i <= n; i ++)    
            scanf("%d", &a[i]), bl[i] = i / bsize + 1;
        for(int i = 1; i <= m; i ++) {
            scanf("%d%d", &qus[i].l, &qus[i].r);
            qus[i].id = i;
        }
        work();
    }
  • 相关阅读:
    kafka 官方 Quickstart
    oracle11.2 安装
    Perl参考函数/教程
    Mysql参见SHOW命令总结
    MySQL的Innodb缓存相关优化
    Oracle、Mysql和SQL Server数据库连接的URL写法
    jredis 客户端 使用
    sql基本命令-存储过程
    NoSql系列目录ElasticSearch-mongodb
    loadrunner 运行场景-Controller及Load Generators宿主主机优化
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9794272.html
Copyright © 2011-2022 走看看