題意:c++
給一個環,環上有n+m個點。給n個點染成B,m個點染成W。求全部染色狀況的每段長度乘積之和。數組
題解:ide
染成B的段數和染成W的段數是同樣的(由於是環)。url
第一段是能夠移動的,例如BBWWW移動爲BWWWB。spa
因此處理兩個方程:b[i][j]表明把j分紅i段的乘積和且第一段不能移動;f[i][j]表明把j分紅i段的乘積和且第一段能夠移動。.net
那麼枚舉分紅的段數,對於當前枚舉分紅i段,答案就爲:f[i][n]*b[i][m]+f[i][m]*b[i][n].code
問題是方程怎麼轉移了。blog
對於b[i][j],枚舉一個新加的數(1~j-i+1),即b[i][j] = 1*b[i-1][j-1]+2*b[i-1][j-2]+...+(j-i+1)*b[i-1][i-1].get
對於f[i][j],肯定他的第一個數是什麼,而後枚舉一個新加的數,即f[1][i] = i*i(肯定第一個數);f[i][j] = 1*f[i-1][j-1]+2*f[i-1][j-2]+...+(j-i+1)*f[i-1][i-1].it
HDU上建2個數組會MLE,因此只能在camp上過2個數組的。camp題目地址:https://www.icpc.camp/contests/6CP5W4knRaIRgU
#include <bits/stdc++.h> using namespace std; const int N = 5001; const int mod = 1e9+7; typedef long long ll; int n, m; ll f[N][N], b[N][N]; ll ans; int main() { b[0][0] = 1; for(int i = 1; i <= 5000; i++) { ll sum = 0, res = 0; for(int j = i; j <= 5000; j++) { res = (res+b[i-1][j-1])%mod; sum = (sum+res)%mod; b[i][j] = sum; } } for(int i = 1; i <= 5000; i++) f[1][i] = (i*i)%mod; for(int i = 2; i <= 5000; i++) { ll sum = 0, res = 0; for(int j = i; j <= 5000; j++) { res = (res+f[i-1][j-1])%mod; sum = (sum+res)%mod; f[i][j] = sum; } } while(~scanf("%d%d", &n, &m)) { ans = 0; int up = min(m, n); for(int i = 1; i <= up; i++) { ans = (ans+f[i][n]*b[i][m]+f[i][m]*b[i][n])%mod; } printf("%lld\n", ans); } }