计数练习题

【不会计数,要吐了】

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)\)

posted @ 2025-08-22 12:03  harmis_yz  阅读(9)  评论(0)    收藏  举报