牛客網暑期ACM多校訓練營(第一場)A Monotonic Matrix LGV定理 E Removal 思維,dp
阿新 • • 發佈:2018-08-03
pri lin mod 不重復 牛客網 contest sdn 。。 namespace
套Lindstr?m–Gessel–Viennot lemma,答案是C(n+m, n)^2 - C(n+m, m-1) * C(n+m, n-1) 。
太蠢了看不懂官方題解,參考大佬博客寫的。。
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 定理
主要用來解決網格圖不相交路徑計數問題。
E Removal
題意:
長度為 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)
太蠢了看不懂官方題解,參考大佬博客寫的。。
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; }
牛客網暑期ACM多校訓練營(第一場)A Monotonic Matrix LGV定理 E Removal 思維,dp