计数练习题
【不会计数,要吐了】
AT_arc174_e Existence Counting
考虑容斥。答案等价于:总的方案数 - 比 \(P\) 大的方案数 - 不包含 \(x\) 的方案数 + 比 \(P\) 大且不包含 \(x\) 的方案数。
第一个东西是 \(A_{n}^k\),第二个是 \(\sum\limits_{i=1}^{n}(n-1-\sum\limits_{j<i}^{}[P_j>P_i])A_{n-i}^{k-i}\),第三个是 \(A_{n-1}^k\),第四个是 \(\sum\limits_{i=1}^{id_x-1}(n-1-\sum\limits_{j<i}^{}[P_j>P_i]-[P_i < x])A_{n-i-1}^{k-i}\)。其中第二个和第四个我们是在枚举第一个比 \(P\) 大的位置,那么这个位置之前的值是一定的,之后的值是可以随便选择的。时间复杂度 \(O(n\log n)\)。
AT_dp_t Permutation
考虑插空。定义状态函数 \(f_{i,j}\) 表示前 \(i\) 个数,最后一个选择的是 \(j\) 的方案数。那么当 \(p_i > p_{i-1}\) 时,\(f_{i,j}=\sum\limits_{k<j}^{}f_{i-1,k}\),反之同理。为什么直接做是可行的呢?因为我们相当于在值域上维护连续段,如果插入了一个数 \(x\),而在当前段已经有了,那么可以将 \(x\sim end\) 全部加 \(1\) 来插入。因为是排列,所以一定可以保证 \(end \le n\)。注意 \(j \le i\) 即可,因为前 \(i\) 个数在值域上的连续段长度为 \(i\),不可能会有数能够比 \(i\) 大。
CF1295F Good Contest
可以直接得到暴力 DP。定义状态函数 \(f_{i,j}\) 表示值 \(1 \sim i\),已经覆盖完 \([j,n]\) 的所有段的概率。因为没有“逆序对”(其实是顺序对),相当于 \(1\sim n\) 选择的值不增,那么就是倒序不降。则:\(f_{i,j}=\sum f_{i-1,k}\prod\limits_{x=k+1}^{j}\frac{1}{r_x-l_x+1}[maxl(k+1,j)\le i \land minr(k+1,j)\ge i]\)。
注意到对于同一组 \((k,j)\),满足 \(maxl(k+1,j)\le i \land minr(k+1,j)\ge i\) 的 \(i\) 是连续的区间,那么将 \(l_i,r_i\) 看作断点后得到的每个区间的转移方程是一致的。则暴力矩阵快速幂优化,时间复杂度 \(O(n^4\log n)\)。组合数可以做到 \(O(n^4)\)。
CF431D Random Task
观察 \([n+1,2n]\) 与 \([(n+1)+1,2(n+1)]\) 中的数发现,后者比前者少个个 \(n+1\),多了个 \(2n+1,2n+2\)。由于 \(2n+2=2(n+1)\),所以当 \((n+1)\) 的二进制下有 \(k\) 个 \(1\) 时,\(2n+2\) 的二进制下也一定有 \(k\) 个 \(1\)。而 \(2n+1\) 会对二进制下有 \(k\) 个 \(1\) 的数量产生 \(\{0,1\}\) 的贡献,所以随着 \(n\) 增大,二进制下有 \(k\) 个 \(1\) 的数的数量不减。
那么我们去二分答案,现在问题就变成了查询 \([n+1,2n]\) 中二进制下有 \(k\) 个 \(1\) 的数的数量,数位 DP 维护即可。
AT_abc180_f Unbranched
根据题意,一个联通块要么是个环,要么是条链。
定义状态函数 \(f_{i,j}\) 表示用了 \(i\) 个点,用了 \(j\) 条边,环大小不超过 \(lim\) 的方案数。那么 \(f_{i,j}=f_{i-k,j-(k-1)}\times C_{n-(i-k)-1}^{k-1}\times k! + f_{i-k,j-k}\times C_{n-(i-k)-1}^{k-1}\times \frac{k!}{2k}\)。
则有至少一个环大小为 \(L\) 就可以变成 \(lim \in\{L-1,L\}\) 时的答案之差了。时间复杂度 \(O(nm^2)\)。
AT_tokiomarine2020_e O(rand)
首先 \(A_i\) 可能被选的充要条件是 \(s \in A_i \in t\)。这里 \(A_i\) 是二进制下 \(1\) 的集合。那么对于 \(t-s\) 中的每一个元素,需要满足我们选择的一些 \(A_i\) 中至少有一个不包含它。
这个是个经典的子集容斥,时间复杂度 \(O(Vn)\)。
const int N=51,M=(1ll<<18);
int n,k,s,t;
int a[N];
int C[N][N],S[N],cnt[M];
il void solve(){
n=rd,k=rd,s=rd,t=rd;
if((t&s)!=s) return puts("0"),void(0);
for(re int i=0;i<=n;++i){
C[i][0]=1;
for(re int j=1;j<=i;++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
for(re int j=1 ;j<=min(i,k);++j) S[i]+=C[i][j];
}
int m=0;
for(re int i=1;i<=n;++i){
a[i]=rd;
if((a[i]|t)==t&&(a[i]&s)==s) a[++m]=a[i];
}
n=m;
int u=(t-s),Res=S[n];
for(re int i=u;i;i=(i-1)&u){
int res=0;
for(re int x=1;x<=n;++x) ++cnt[a[x]&i];
for(re int x=1;x<=n;++x){
if(!cnt[a[x]&i]) continue;
res+=S[cnt[a[x]&i]],cnt[a[x]&i]=0;
}
if(__builtin_popcount(i)&1) Res-=res;
else Res+=res;
}cout<<Res;
return ;
}
AT_mujin_pc_2018_f チーム分け
我还不会,但是我能过。
首先 \(a\) 从大到小排序。那么 \(i\) 的限制肯定比 \(i\sim i-1\) 都强,也就是如果 \(i\) 能放进一个大小为 \(x\) 的组中,那么这个组的 \(x\) 个数一定也满足条件。
所以问题变成,将序列分组,使得每组最后一个数的值不小于组的大小。定义状态函数 \(f_{i,j}\) 表示前 \(i\) 个数,有 \(j\) 个数已经被分掉的方案数。那么 \(f_{i,j}=f_{i-1,j}+\sum\limits_{k=0}^{a_i-1}f_{i-1,j-k-1}C_{i-1-j}^k\)。答案为 \(f_{n,n}\),时间复杂度 \(O(n^3)\)。

浙公网安备 33010602011771号