题解/算法 {E - Alphabet Tiles}
@LINK: https://atcoder.jp/contests/abc358/tasks/abc358_e
;
定义: 我们称一个字符串S是合法的, 当其中每个字符出现的次数 是[0, Ci]
范围的(Ci
是题目给定的);
这个题 没找到正确思路的 错误做法还是很多的… 比如你可能想到不同颜色小球的排列数 即比如C1,C2,C3
表示不同颜色小球的个数 那么其排列数是(C1+C2+C3)! / C1! / C2! / C3!
, 但是 他的前提是 你每种颜色的个数C1,C2
是固定的确定的, 可是 这个题里 他的个数是[0, Ci]
范围 是个变数;
我们直接讲正确做法;
DP(i,j): i[0,25], j[0,K]
由'A' + 0,1,...,i
这些字符构成长度为j
的合法字符串的个数;
然后对于(i,j)
我们枚举再往里面 加入x个(i+1)元素
(0<= x <= C[i+1]
), 比如A1, A2, ..., Aj
我们要往里面 再加入x
个B
(B != Ai
); 比如j=2, x=2
那么会有[B,B,A1,Aj], [B,A1,B,Aj], [B,A1,Aj,B], [A1,B,B,Aj], [A1,B,Aj,B], [A1,Aj,B,B]
);
这个问题 等价于是: 将这个x
这个元素 放到原序列A1...Aj
的缝隙里面 他有j+1
个缝隙, 也就是隔板法Split0
;
时间是26 * 1e3 * 1e3
, 因此隔板法要做到O(1)
你要预处理二维数组;
最终答案是DP[25][ 1|2|...|K]
之和;
int K; cin>> K;
vector<int> Cont( 26); for( auto & i : Cont){ cin>> i;}
Mod_ DP[ 26][ 1003];
std::memset( DP, 0, sizeof( DP));
FOR_( I, 0, 25){
if( I == 0){
FOR_( J, 0, Cont[I]){
DP[ I][J] = 1;
}
continue;
}
FOR_( Jpre, 0, K){
FOR_( cont, 0, std::min( Cont[I], K-Jpre)){
DP[ I][ Jpre + cont] += (DP[I-1][Jpre] * ___Combinatorics_Split0<Mod_, int>( cont, Jpre+1));
}
}
}
Mod_ ANS = 0;
FOR_( i, 1, K){ ANS += DP[25][i];}
cout<< ANS.Value<< "\n";