「some」组合杂题瞎做
卡农
虽然oj上放在了状压的专题里,但它跟状压一点关系都没有。
算是补老早之前的坑了
题意:从集合 \(S\) 中选出 \(m\) 个非空子集,要求任意两个子集不能相同,且每个元素的出现次数为偶数次,问你满足条件的方案数。
首先,将选的集合转化为有序,那么最后的答案只需要除以 \(m!\) 就可以了。思路来自学长课件
若确定了前 \(m-1\) 个子集,则第 \(m\) 个子集的作用便是去将前边出现次数为奇数次的元素补成偶数次,所以第 \(m\) 个子集也是被确定的。
考虑dp,设 \(dp_{i}\) 表示前 \(i\) 个集合,合法情况的个数。此时,若只考虑偶数次这一条件,则前 \(i-1\) 个 集合的方案数为 \(\mathrm{A}_{2^{n}-1}^{i-1}\) ,考虑其他条件的限制。
非空:
若第 \(i\) 个子集为空,则前 \(i-1\) 个子集已经构成了合法的方案。则不合法的方案数即为 \(dp_{i-1}\) 。
重复:
将其中的重复子集拿出来,则剩下 \(i-2\) 个,则重复的子集的种类即为 \(2^{n}-1-(i-2)\) ,枚举重复的子集的位置,有 \(i-1\) 种,则不合法的方案数为 \(dp_{i-2}\times (2^{n}-1-(i-2))\times (i-1)\) 。
则最后得到 \(dp_{i}\) 的表达式,
答案即为 \(\frac{dp_{m}}{m!}\) 。
然后此题就无了难点,但我菜,所以还是要给自己说一下。
观察到数据范围,\(n,m\le10^{6}\) ,显然,如果我们直接去计算 \(2^{n}-1\) 的阶乘是不可行的。
再观察最后 \(dp_{i}\) 的式子,发现其中 \(\mathrm{A}_{2^{n}-1}^{i-1}\) 这一项中,随着 \(i\) 发生变化的仅有 \(i-1\) 这一项,所以我们考虑直接预处理出所有的 \(\mathrm{A}_{2^{n}-1}^{i-1}\) 。
如何直接求这一项? 把排列的式子化简一下就好了。
CF1264D2 Beautiful Bracket Sequence
正在写
因为改不出来Rectangle而来氵blog的屑
对于一个已知的括号序列,发现从头到尾选取匹配的括号删掉,知道不可删为止,删掉的对数便是最终的答案。
考虑从左侧开始选取 \((\) 一个个和右侧的 \()\) 开始匹配,最后得到的答案一定是最优。
每种情况删完后,必然是一个合法的括号序列,所以考虑枚举中心点 \(p\) ,设 \(p\) 左侧的 \((\) 数量为 \(a\) ,右侧的 \((\) 数量为 \(b\),\(p\) 左侧的 \(?\) 数量为 \(c\) ,右侧的 \(?\) 数量为 \(d\) 。
则对于当前中心点 \(p\) ,考虑枚举左侧的 \(?\) 有 \(i\) 个变成了 \((\) ,则此时 \((\) 有\(a+i\) ,选择方案数为 \(\tbinom{c}{i}\),右侧则有 \(a+i-b\) 个 \(?\) 变成了 \()\) ,方案数为 \(\tbinom{d}{a+i-b}\) ,相乘求和即是当前点的答案,即为:
然而 \(n\le10^{6}\) ,显然会T。
对式子进行一波化简则有,
最后用范德蒙恒等式进行化简,
之后枚举每个中心点 \(p\) 累加答案即可。
Code
#include<cstdio>
#include<cstring>
#define MAX 1000010
#define re register
#define int long long
namespace OMA
{
int n,ans;
char s[MAX];
int jc[MAX],inv[MAX];
int a[MAX],b[MAX],c[MAX],d[MAX];
const int p = 998244353;
inline int quickpow(int a,int b)
{
int ans = 1;
while(b)
{
if(b&1)
{ ans = ans*a%p; }
a = a*a%p;
b >>= 1;
}
return ans;
}
inline int C(int n,int m)
{ return m>n||m<0||n<0?0:jc[n]*inv[n-m]%p*inv[m]%p; }
signed main()
{
scanf("%s",s+1);
n = strlen(s+1);
jc[0] = inv[0] = 1;
for(re int i=1; i<=n; i++)
{ jc[i] = i*jc[i-1]%p; }
inv[n] = quickpow(jc[n],p-2);
for(re int i=n-1; i; i--)
{ inv[i] = (i+1)*inv[i+1]%p; }
for(re int i=1; i<=n; i++)
{ a[i] = a[i-1]; if(s[i]=='('){ a[i]++; } c[i] = c[i-1]; if(s[i]=='?'){ c[i]++; } }
for(re int i=n; i; i--)
{ b[i] = b[i+1]; if(s[i]==')'){ b[i]++; } d[i] = d[i+1]; if(s[i]=='?'){ d[i]++; } }
for(re int i=1; i<=n-1; i++)
{ (ans += (a[i]*C(c[i]+d[i+1],d[i+1]-a[i]+b[i+1])%p+c[i]*C(c[i]+d[i+1]-1,d[i+1]-a[i]+b[i+1]-1)%p)%p) %= p; }
printf("%lld\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }