牛客网暑期ACM多校训练营(第一场)A Monotonic Matrix LGV定理 E Removal 思维,dp

A Monotonic Matrix

题意:
构造满足下列3 点的矩阵,问有多少种方案。

  • Ai, j ∈ {0, 1, 2} for all 1 ≤ i ≤ n, 1 ≤ j ≤ m.
  • Ai, j ≤ Ai + 1, j for all 1 ≤ i < n, 1 ≤ j ≤ m.
  • Ai, j ≤ Ai, j + 1 for all 1 ≤ i ≤ n, 1 ≤ j < m.

题解:
考虑01 和12 的分界线,是(n, 0) 到(0, m) 的两条不相交(可重合)路径。
平移其中一条变成(n-1, -1) 到(-1, m-1),变成起点(n, 0) 和(n-1, -1),终点(0, m) 和(-1, m-1) 的严格不相交路径。
套Lindström–Gessel–Viennot lemma,答案是C(n+m, n)^2 - C(n+m, m-1) * C(n+m, n-1) 。

Lindström–Gessel–Viennot lemma 定理

主要用来解决网格图不相交路径计数问题。


###[E Removal](https://www.nowcoder.com/acm/contest/139/E) 题意: 长度为 n 的字符串,每个字符 1<=si<=k,要恰好删去 m 个字符,问最后留下的字符串有多少种可能。 题解: 官方题解:设next(i, c) 表示位置i 后第一个字符c的位置f(i, j) 表示当前匹配到i,删了j 个,不同的方案数转移时枚举下一个字符c,转移到f(next(i, c), j+ next(i, c) -i)
太蠢了看不懂官方题解,参考[大佬博客](https://blog.csdn.net/litmxs/article/details/81141606)写的。。

dp[i][j] 表示前 i 个字符,删除了 j 个字符的不重复方案数。
如果允许重复,很容易想到转移为 dp[i][j] = dp[i-1][j] + dp[i-1][j-1] 。
再想哪些是重复了呢?比如 {1,1,2,3,2 }, 我们删除第4和第5个数{3,2} ,或者删除第3和第4个数 {2,3} ,这就重复了。也就是说,对于两个相同的数,如果它们中间有数字,那就会造成一次重复。
所以用 pre[i] 表示前一个 s[i] 的位置,dp[i][j] 减去 dp[pre[s[i]]-1] [j-(i-pre[i])] 即可。


#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 100005, mod = 1e9+7;

int n, m, K, s[N], pos[11], pre[N];
ll  dp[N][11];
void Init() {
    rep(j,0,10) pos[j] = 0;
    rep(i,0,N-1) {
        rep(j,0,10) dp[i][j]=0;
    }
    dp[0][0] = 1;
}
int main()
{
    while(~scanf("%d%d%d", &n, &m, &K))
    {
        Init();
        rep(i,1,n) {
            scanf("%d", &s[i]);
            pre[i]=pos[s[i]],  pos[s[i]]=i;
        }
        rep(i,1,n)
        {
            rep(j,0,m)
            {
                ( dp[i][j] += dp[i-1][j] ) %= mod;
                if(0<=j-1)
                    ( dp[i][j] += dp[i-1][j-1] ) %= mod;
                int num = j-(i-pre[i]);
                if(0<=num && 0<pre[i])
                    ( dp[i][j] -= dp[pre[i]-1][num] ) %= mod;
            }
        }
        printf("%lld\n", (dp[n][m]+mod)%mod);
    }

    return 0;
}
posted @ 2018-08-03 11:06  v9fly  阅读(217)  评论(0编辑  收藏  举报