题目总结
- G - Takahashi And Pass-The-Ball Game
- F - 1122 Subsequence 2
- F - Many Many Paths
- G - Teleporting Takahashi
- CodePlus 2018 4 月赛 组合数问题 2
- SHOI2015 超能粒子炮 改
- 清华集训2016 组合数问题
- B - 123 Triangle
- F - Maximum Diameter
- USACO 2025 US Open Gold Problem 1 Moo Decomposition
- NOIP2025 清仓甩卖
- 信封问题
- 分特产
- 硬币购物
- 集合计数
- 水仙花
- 已经没有什么好害怕的了
- Positions in Permutations
- 过河卒二
- 方案数
- 游戏
- 串珠子
- Combinatorics Problem
- Zapina
- Impreza krasnali 2
- Problem c
- Malowanie płotu
- Piratski kod
- 帐篷
G - Takahashi And Pass-The-Ball Game
G - Takahashi And Pass-The-Ball Game
1.看到期望不要怕,先分析,就能看出来这题和期望也没啥关系,就是把期望转换成其他的
2.看到一个图上倍增的好方法,类似快速幂
vector<int> move(vector<int> a,vector<int> b)
{
vector<int> res(n);
for(int i=0;i<n;i++) res[i]=a[b[i]];
return res;
}
vector<int> mo(vector<int> a,vector<int> x)
{
vector<int> res(n);
for(int i=0;i<n;i++) res[a[i]]=(res[a[i]]+x[i])%mod;
return res;
}
vector<int> add(vector<int> x,vector<int> y)
{
for(int i=0;i<n;i++) x[i]=(x[i]+y[i])%mod;
return x;
}
vector<int> ans(n);
b=mo(a,b);
while(k)
{
if(k&1)
{
ans=add(ans,b);
b=mo(a,b);
}
b=add(b,mo(a,b));
a=move(a,a);
k>>=1;
}
F - 1122 Subsequence 2
1.看到\(\sum_{i=1}^{i<=min(n,m)}C_n^iC_m^i\)这类式子可以将前一个式子用对称性转一下,然后用范德蒙德卷积优化
F - Many Many Paths
1.区间如果想用上指标求和,要先转成前缀和相减的形式,不能直接用区间长度
G - Teleporting Takahashi
1.推式子比较长,但实际并不难,不要怕推式子(推式子详见秦路岩博客)
CodePlus 2018 4 月赛 组合数问题 2
1.组合数判断大小不能在组合数内取模,由于两个数相除等于他们的log相减(减完也是log下的),所以可以转换为组合数的log相减
SHOI2015 超能粒子炮 改
1.思想大概是把贡献分块来算(感觉是很好的思路,细节很多)
2.灵活运用Lucas?
清华集训2016 组合数问题
1.k很小,考虑用Lucas
2.lucas实际上是把n,m转化为k进制
3.那么\(C_a^b\%k≡0\)等价于\(k\)进制下存在某一位\(C_{a_i}^{b_i}≡0\),由于k是质数,因此只有\(a_i<b_i\)的情况\(C_{a_i}^{b_i}=0\)同余0
4.我们问题转变成了,k进制下,对于\(0\leq i \leq n,0\leq j \leq min(i,m)\),有多少个\(ij\)满足\(k\)进制下存在某一位\(a_i<b_i\),数位dp之
B - 123 Triangle
1.先去绝对值,发现和二项式定理差不多,然后发现和奇偶性相同,然后去绝对值,拿到答案(详见lhh博客)
F - Maximum Diameter
1.首先,注意到钦定将所有度数大于一的和两个叶子构成一条链一定是最优的
2.接着,从叶子节点入手,钦定有k个叶子,挂到非叶子节点上即可,总度数为\(2n-2\),减去叶子,为\(2n-2-k\),让每个非叶子节点度数减一,再用挡板法,总度数为\(2n-2-k-(n-k)=n-2\)
,总方案为\(C_n^kC_{n-3}^{n-k-1}\),直径为\(n-k+1\)
3.对上面的式子化简,详见题面日本语
USACO 2025 US Open Gold Problem 1 Moo Decomposition
[USACO 2025 US Open, Gold] Problem 1. Moo Decomposition
(纯水题)
1.看到题面先把玩两下样例,发现如果两个M在一起,那么后面的O可以两个一起选,如果两个M之间有O那么这些O必须给前一个M,也就是说M只能从它后面找O(好像是废话)
2.考虑正着做时,不能从后面所有的里面选k个,正难则反,考虑倒着枚举每个M,记录一下它后面剩了几个O,从剩的里面选k个,更新剩了多少,遍历完拿到ans,最后求一下\(ans^l\)即可
NOIP2025 清仓甩卖
(这是啥阴间题)
1.首先,合法方案不好算,考虑正难则反,用总方案减去不合法方案
2.不合法的方案可能是之前选了一个w=1的物品,后面没钱选w=2的物品,而选了一个很弱的物品,导致两个加起来不如选w=2的
3.令第一个w=1的物品为特殊物品,w=2的为正确物品,那么易得\(a_x<a_y<2*a_x\)
4.对于性价比大于 y 的物品,我们一定会选择。
5.对于原价已经\(a_y\)的物品,无论价格总会被选,会消耗1或2元。这样的物品有n−y个。
6.对于原价在\(a_x\)和\(a_y\)之间的物品,w=1 时性价比优于y会被选,会消耗0或1元。这样的物品有\(y−x−1\)个。
7.对于原价在\(a_y/2\)和\(a_x\)之间的物品,由之前错解的形态可知一定不会被选,只能w=2并消耗0元。
8.也就是说,上述所有物品中我们会花掉m−2 元钱。先扣掉1/2元中的1元,变成在n−x−1个物品中选m−2−(n−y)个花掉1元。组合数可以O(1)计算。
9.对于原价小于\(a_x\)的部分,我们只需要让\(a_p+a_x≥a_y\)的部分都不成为z即w=2,剩余的随便选即可。
10.相当于找到最大的p满足\(a_p+a_x<a_y\),前面的方案数即为\(2^p\)。p显然可以在枚举y时使用双指针求出。
(如果考试遇到这题可以跳了)
信封问题
1.错排板子,没啥可说的
公式:\(D(n)=(n-1)*(D(n-1)+D(n-2))\)
分特产
1.这里就是二项式反演,进行容斥
2.用\(f[i]\)表示至少有i个同学没有土特产,\(g[i]\)表示刚好有i个同学没有土特产,求g考虑二项式反演
2.分别处理每个特产分出去的方案数,最后的乘积就是答案,然后由于强制哪个同学也不确定,而规定i个同学不能选到的方案数其实就是从n个人里面选出i个人的组合数,综上\(f[i]=C_n^i*\prod_{j=1}^mC_{a[j]+n-i-1}^{n-i-1}\)
3.答案ans即为g[0],\(ans=\sum_{i=0}^{n-1}(-1)^iC_n^i*\prod_{j=1}^mC_{a[j]+n-i-1}^{m-i-1}\)
硬币购物
1.正难则反,考虑用总的方案减去不合法的方案
2.总的方案数就是完全背包,然后容斥,减去至少一个不合法,加上至少两个不合法,减去三个,加上四个
3.考虑不合法的方案数怎么求,状压有哪些不合法的,计算就是钦定先选出来\(d[i]+1\)个\(c[i]\)的硬币,剩下的随便,即剩下的用完全背包
集合计数
1.二项式反演板子,钦定\(f[i]\)表示选恰好为i个元素在交中的方案数,\(g[i]\)表示至少有i个元素在交中的方案数
2.考虑\(g[i]\)如何求,首先我们需要从n个元素中选择i个元素,方案数有\(C_n^i\)种,然后对于剩下 n−i 个元素,我们可以列举出可能存在的集合的可能,也就是\(2^{n−i}\)种可能,对于这些集合我们可以选可以不选,共有\(2^{2^{n-i}}\)种,但是题目要求至少选一个,所以要去掉空集,为\(2^{2^{n-i}}-1\)。
3.接着列出g[i],f[i]之间的关系,\(g[i]=\sum_{j=i}^nC_j^if[j]\)
4.反演,得出f[i]的公式,\(f[i]=\sum_{j=i}^n(-1)^{j-i}C_j^iC_n^j(2^{2^{n-i}}-1)\)
水仙花
1.考虑到最大的连续不合法段最多有20个元素,考虑dp,设\(f[i][j]\)表示长为i,最后一个元素为j的不合法方案数
for(int j=1;j<=m;j++) f[1][j]=1;
num[1]=m;
for(int i=2;i<20;i++)
for(int j=1;j<=m;j++)
{
for(int k=j*2;k<=m;k+=j)
f[i][j]=(f[i][j]+f[i-1][k])%mod;
num[i]=(num[i]+f[i][j])%mod;
}
2.dp[i]表示长为i的合法方案,用总的方案数(即长为1的),减去长为2的,加上长为3的.....
dp[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=max(1ll,i-18);j<=i;j++)
{
dp[i]+=(!((i-j+1)&1)?-1:1)*dp[j-1]*num[i-j+1]%mod;
}
dp[i]%=mod;
}
已经没有什么好害怕的了
1.首先,对a,b数组排序是显然的,我们假设a>b对数为x,可以求得\(x=(n+k)/2\)
2.设dp[i]表示前i个a中,选了j组满足\(a>b\)的方案数,容易得到dp方程\(dp[i][j]=dp[i-1][j]+(xiao[i]-(j-1))*dp[i-1][j-1]\)
其中,\(xiao[i]\)表示从小到大排序b后\(<a[i]\)的最后一个数
3.用g[i]表示\(a>b\)的组数至少为i的方案数,f[i]\(a>b\)的组数恰好为i的方案数,\(g[i]=f[n][i]*(n-i)!\)
4.用二项式反演求出f[i]
Positions in Permutations
1.显然,二项式反演,设f[i]表示恰好有i个数是完美数,g[i]表示至少有i个数wei完美数
2.考虑g[i]如何求解,设\(f[i][j][0/1][0/1]\)表示前i个位置,选了j个完美数,数i选择的情况,数i+1选择的情况,状态转移
位置i填i-1,要保证i-1在之前没被选过
- \(dp[i][j][0][0]+=dp[i-1][j-1][0][0]\)
- \(dp[i][j][1][0]+=dp[i-1][j-1][0][1]\)
位置i填i+1,要保证i+1在之前没被选过
- \(dp[i][j][0][1]+=dp[i-1][j-1][0][0]+dp[i-1][j-1][1][0]\)
- \(dp[i][j][1][1]+=dp[i-1][j-1][0][1]+dp[i-1][j-1][1][1]\)
位置i不填数,不让它成为完美位置
- \(dp[i][j][0][0]+=dp[i-1][j][0][0]+dp[i-1][j][1][0]\)
- \(dp[i][j][1][0]+=dp[i-1][j][0][1]+dp[i-1][j][1][1]\)
3.g[i]即为\((f[n][i][0][0]+f[n][i][1][0])*(n-i)!\)
过河卒二
1.从(1,1)到n+1行或m+1列,等价于到(n+1,m+1)位置的所有方案
2.考虑将所有坐标减一,即求(0,0)到(n,m)的方案数,把x作为第一关键字排序,y作为第二关键字排序
3.考虑设\(f[i]\)表示从(0,0)到第i个不合法位置,之前没有到过不合法位置的方案数,考虑枚举i表示走了多少步向右上的,那么横着还能走x-i步,竖着走y-i步,再加上这i步,总共有\(x-i+y-i+i=x+y-i\)步,从这里面选i步,再乘上从剩的步数里选\(x-i\)步横着走,这是总的方案,再减去不合法的,即之前走过不合法位置的方案,即枚举f[j]为之前遍历的不合法位置,那么正确的式子即为
4.注意1e9的阶乘无法计算,要用Lucas
方案数
1.记 x 二进制表示下 1 的个数为 count(x) ,g[i][j][k] 为从 (0,0,0) 到满足 count(x)=i,count(y)=j,count(z)=k 的点 P(x,y,z) 的方案数
2.dp转移式为
3.接下来求解方案数即可,记得容斥
int work(int x,int y,int z,int xx,int yy,int zz)
{
if((x&xx)!=x||(y&yy)!=y||(z&zz)!=z) return 0;
int num1=0,num2=0,num3=0;
int now=x^xx;
while(now)
{
num1++;
now-=lowbit(now);
}
now=y^yy;
while(now)
{
num2++;
now-=lowbit(now);
}
now=z^zz;
while(now)
{
num3++;
now-=lowbit(now);
}
return g[num1][num2][num3];
}
for(int i=1;i<=o;i++)
{
f[i]=work(0,0,0,a[i].x,a[i].y,a[i].z);
for(int j=1;j<i;j++)
f[i]=(f[i]-f[j]*work(a[j].x,a[j].y,a[j].z,a[i].x,a[i].y,a[i].z)%mod)%mod;
}
cout<<f[o]<<endl;
游戏
游戏
1.(这题不知到摆个期望是何意味,不是让求方案数吗)
2.就是二项式反演,用树形dp求出f[i],反演出g[i]即可,这里就不再赘述了
串珠子
1.状压,用 dp[k] 表示 k 这个点集的答案(联通连边方案数)。用 f[k] 表示 k 这个点集的连边方案数。用 ♠(k) 表示 k 这个点集不连通连边方案数。这时求 ♠(k) 需要先找一个 k 中的点 p(为了确保计算 ♠(k) 时拆成的\(C_ki\)有点,且拆分方案不会重复计算),求出\(frm=C_k{p}\),其中C为补集符号,下同。
2.\(♠(k)=\sum_{i∈frm}f[i]×dp[C_ki]\)
(拆出\(C_ki\),剩下的点随便拆不拆。)
3.\(f[k]=\prod_{(i<j)∈k}(c[i][j]+1)\)
4.\(dp[k]=f[k]−♠(k)\)
5.答案即为\(dp[2^n-1]\)
Combinatorics Problem
Zapina
1.设dp[i][j]表示表示前i个人分配j道题且至少已经有一个人开心的方案数
2.使i开心:随便給他i道,剩下j−i道随便分給剩下的i−1个人。 方案数:\(C(j,i)×(i−1)^{j−i}\)
3.使i不开心:随便給他k道,剩下j−k道给i−1个人并使这 i−1 个人中有人开心。 方案数:\(C_j^k×dp[i−1][j−k]\)
Impreza krasnali 2
1.首先,观察到有如下性质:
- 如果\(a_{i−2}=a_i\),则\(a_i\)必然放在i−1这个位置上;如果\(a_{i−1}=a_i\),则\(a_i\)必然放在i−1或者i
- 如果\(j<i−1\),则最终\(a_j\)必然不会放在\(a_ i\)的后面。
2.那么就可以考虑dp,设只考虑前i个的方案数为\(f_{i,0/1/2/3/4}\),有如下几种情况:
- \(a_i\)放在位置i−1上,且\(a_ {i−1}\)不在 \(a_i\)的后面,这种情况的方案数记为\(f_{i,0}\)
- \(a_i\)放在位置i−1上,且\(a_{i−1}\)放在 i 上,记为\(f _{i,1}\)
- \(a_i\)放在 i 上,记为\(f_{i,2}\)
- \(a_i\)放在i+1上,且\(a_{i−1}\)放在 i 上,记为\(f_{i,3}\)
- \(a_i\)放在 i+1 上,且\(a_{i−1}\)不放在 i 上,记为\(f_{i,4}\)
3.转移
- \(a_{i−2}=a_{i−1}=a_i\),显然\(a_i\)只能要放在 i−1,转移
- \(f[i][0]=f[i-1][2]\)
- \(a_{i−2}=a_i\),且\(a_{i−1}!=a_i\),那么\(a_i\)还是只能放在 i−1,转移
- \(f[i][0]=f[i-1][1]\)
- \(f[i][1]=f[i-1][3]\)
- 若\(a_i==a_{i-1}!=a_{i-2}\)
- \(f[i][0]=f[i-1][2]\)
- \(f[i][2]=f[i-1][3]+f[i-1][4]\)
- 若\(a_i!=a_{i-1}!=a_{i-2}\)
- \(f[i][0]=f[i-1][0]\)
- \(f[i][1]=f[i-1][4]\)
- \(f[i][2]=f[i-1][1]+f[i-1][0]+f[i-1][2]\)
- \(f[i][3]=f[i-1][3]+f[i-1][4]\)
- \(f[i][4]=f[i-1][0]+f[i-1][1]+f[i-1][2]\)
4.最终答案是 \((n-c)!(f_{n,0}+f_{n,1}+f_{n,2})\)
Problem c
1.先考虑无解的情况,我们记s[i]表示已经确定的m人中编号≥i的人数。那么如果存在 \(s[i]>n−i+1\),显然无解
2.设\(f[i][j]\)表示表示剩余n−m人中编号≥i的人已经确定j个人的编号的方案数,转移即为:
- \(f[i][j]=\sum_{k=0}^jf[i+1][j-k]*C_j^k(0<=j<=n-s[i]-i+1)\)
即为从j个人中选k个人,在已经确定j−k人编号的情况下,再选择k人确定编号为i
3.答案即为\(f[1][n-m]\),时间复杂度为\(O(Tn^3)\)
Malowanie płotu
1.这题实际上就是对暴力dp的优化,暴力就是设\(f_{i,j}\)表示最后选的区间为\([i,j]\)时的方案数(这是滚动数组,用来节省空间),转移
- \(f_{i,j}=\sum\limits_{[x,y]\cap[i,j]\neq\varnothing}f_{x,y}\)
2.考虑容斥,注意到不满足条件的方案必须满足如下条件之一
- \(y<i\)
- \(j<x\)
所以可以用总方案减去不满足条件的
-
\[f_{i,j}=\sum_{1\leq x\leq y\leq m}f_{x,y}-\sum_{1\leq x\leq y<i}f_{x,y}-\sum_{j<x\leq y\leq m}f_{x,y} \]
3.我们设 \(sl_i=\sum_{1\leq x\leq y \leq i}f_{x,y}\) , \(sr_i=\sum_{i\leq x\leq y\leq m}f_{x,y}\)
那么\(f_{i,j}=sl_m-sl_{i-1}-sr_{j+1}\)
4.考虑进一步优化状态:
设\(F_i\) 表示当前区间右端点为 \(i\) 的所有合法方案之和,\(g_i\) 表示当前区间左端点为 \(i\) 的所有合法方案之和
其中
- \(sl\) 和 \(sr\) 均可以用前缀和维护
Piratski kod
一.
定义整块为字符串末尾是两个相邻1的字符串,定义散块为整个串中没有相邻的1的字符串
二.设置状态
- 设\(f[i][0/1]\)表示所有长度为\(i\)的散块结尾为\(0/1\)的价值和
- \(num[i][0/1]\)表示长度为\(i\)的散块结尾为0/1的个数
- \(dp[i]\)表示所有长度为\(i\)的整块的价值和
- \(cnt[i]\)表示长度为\(i\)的整块的个数
三.状态转移
1.散块转移
-
num数组的转移
- \(num[i][0]=num[i-1][0]+num[i-1][1]\)
- \(num[i][1]=num[i-1][0]\)
-
f数组的转移
- \(f[i][0]=f[i-1][0]+f[i-1][1]\)
- \(f[i][1]=f[i-1][0]+fib[i+1]*num[i-1][0]\)
数组下标从0开始,i是长度,因此是\(fib[i+1]\)
2.整块转移
我们考虑一个长度为\(i\)的整块,把它拆成:前面一个整块+最后一个整块
最后一个整块可以写成:
长度为\(j\)且末尾为\(1\)的散块\(+1\)
它的长度为\(j+1\),价值是\(f[j][1]\),个数是\(num[j][1]\)
前面整块的长度就是\(i-(j+1)=i-j-1\)
-
\(cnt\)数组的转移
- \(cnt[i]=\sum_{j=1}^{i-1}cnt[i-j-1]*num[j][1]\)
-
\(dp\)数组的转移
- \(dp[i]=\sum_{j=1}^{i-1}(dp[i-j-1]*num[j][1]+f[j][1]*cnt[i-j-1])\)
总价值 = 前面整块贡献 + 最后一个整块贡献
四.最后答案
最后的串=一个整块+一个散块
\(ans[i]=\sum_{j=2}^idp[j]*(num[i-j][0]+num[i-j][1])\)
帐篷
所有帐篷只有下面三种放法
1.一个帐篷单独占一行和一列,此时这个帐篷入口朝哪里都行
2.两个帐篷在同一行,入口互相朝着对方,且这两列不能再放置其他帐篷
3.两个帐篷在同一列,入口互相朝着对方,且这两行不能再放置其他帐篷
我们设\(dp[i][j]\)表示在i行j列的矩阵里放置帐篷的方案数
转移:
-
第i行不放置帐篷:
- \(dp[i][j]+=dp[i-1][j]\)
-
第i行只放一个帐篷,且这一行没有其他帐篷,那么这个帐篷入口朝哪里都行:
- \(dp[i][j]+=dp[i-1][j-1]*j*4\)
- 为什么是j-1呢,是因为选出来的这一列不能放,还剩j-1列,把它们合成一个块即可
-
第i行放1个帐篷,且这一列前i−1个位置也选择一个位置放置帐篷:
- \(dp[i][j]+=dp[i-2][j-1]*j*(i-1)\)
- 还剩i-2行和j-1列
-
.第i行放2个帐篷,那这两列都不能再放帐篷:
- \(dp[i][j]+=dp[i-1][j-2]*C_j^2\)
- 还剩i-1行和j-2列
最后因为不能一个帐篷也不放,所以输出\(dp[n][m]-1\)

浙公网安备 33010602011771号