省选不杂题乱写(2)
BZOJ 2863 愤怒的元首(DP,二项式反演)
\(n\)个点的\(\text{DAG}\)进行计数。图无需连通,\(n \leq 3000\)。
我们考虑\(\text{DAG}\)的性质:在\(\text{DAG}\)上解决问题的时候,我们通常使用拓扑排序:这样相当于把图变成了一层一层,每次去掉了入度为\(0\)的点的一层。
考虑二项式反演。设\(f_{i,j}\)表示\(i\)个点,钦定了其中\(j\)个点入度为\(0\)的统计方案数,再设\(g_{i,j}\)表示\(i\)个点,其中\(j\)个点入度为\(0\)的方案数。则:
由二项式反演,得:
另外,\(\color{orange}{我们需要满足在}\)统计\(\color{orange}{和}\)钦定\(\color{orange}{这两步中,}\)钦定\(\color{orange}{这一步的计算是准确的。}\)
设\(a_i\)表示\(i\)个点组成的\(DAG\)个数,则:
又:
于是得到了递推式。
原来还能这么写啊(
BZOJ 4008 [HNOI2015]亚瑟王(DP,概率期望)
我的意见是,我没意见.jpg
很容易想到,计算每张卡牌\(被选择的概率\)(或者更好算的,\(1-不选择的概率\))。
比如说像第一张牌,被选择的概率为:
\(A_1=1-(1-P_1)^r\)
因为是这样的:每一轮都没有选择第\(1\)张卡,则每一轮都要考虑第\(1\)张卡能否发动这件事。
也就是看看 第\(i\)张被考虑是否发动 的次数
再思考一下,发现:
-
如果在某一轮中,卡\(i\)前面的某张卡成功发动,则卡\(i\)在该轮一定被考虑。
-
否则,卡\(i\)一定不会被考虑。
所以前\(i-1\)张卡成功发动的次数\(j\)决定了\(i\)的被考虑次数\(r-j\)!
设\(f_{i,j}\)表示前\(i\)张卡里有\(j\)张被选择的概率,则有:
再看看\(f\)怎么转移。
- 选了第\(i\)张
- 不选第\(i\)张:
>w<
「SHOI2017」组合数问题(DP,组合意义,矩阵加速)
给定\(n\),\(p\),\(k\),\(r\),求下柿的值。
暴力拆柿饼无果,看看组合意义。
求:从\(nk\)个元素中取出模\(k\)意义下\(r\)个元素的方案数。
设\(f_{i,j}\)表示\(i\)个元素中取出模\(k\)意义下\(j\)个元素的方案数。则:
由于转移只涉及相邻两行,\(k\)、\(r\)比较小,可以矩阵加速掉。转移矩阵\(\text{E}\)如下:
好像要特判\(k=1\).........
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN 51
long long mod;
int n,k,r;
struct Mar
{
int n,m;
long long a[MAXN][MAXN];
Mar() { for (int i=0;i<MAXN;i++) for (int j=0;j<MAXN;j++) a[i][j]=0; }
}O,E;
Mar operator * (const Mar &A,const Mar &B)
{
Mar C;
C.n=A.n; C.m=B.m;
for (int k=1;k<=B.n;k++)
for (int i=1;i<=C.n;i++)
for (int j=1;j<=C.m;j++)
C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j]%mod)%mod;
return C;
}
inline Mar mqpow(Mar A,long long B)
{
Mar ANS=O,base=A;
while (B)
{
if (B&1) ANS=ANS*base;
base=base*base; B>>=1;
}
return ANS;
}
inline long long qpow(long long A,long long B)
{
long long ANS=1,base=A;
while (B)
{
if (B&1) ANS=ANS*base%mod;
base=base*base%mod; B>>=1;
}
return ANS;
}
/*
inline void Mprint(Mar A)
{
puts("----------");
for (int i=1;i<=A.n;i++,cout<<endl)
for (int j=1;j<=A.m;j++)
printf("%d ",A.a[i][j]);
puts("----------");
}
*/
int main()
{
scanf("%d%lld%d%d",&n,&mod,&k,&r);
if (k==1) return printf("%lld\n",qpow(2,1ll*n*k)),0;
E.n=E.m=k;
for (int i=1;i<k;i++)
E.a[i][i]=E.a[i][i+1]=1;
E.a[k][1]=E.a[k][k]=1;
// Mprint(E);
O.n=O.m=k;
for (int i=1;i<=k;i++)
O.a[i][i]=1;
Mar A;
A.n=1; A.m=k; A.a[1][1]=1;
Mar ans=A*mqpow(E,1ll*n*k);
// Mprint(ans);
printf("%lld\n",ans.a[1][r+1]);
return 0;
}
[IOI2005]Riv 河流(树形背包,加维解决后效性)
泛化物品背包。对于某一个\(\color{orange}{泛化物品,在给其\ w\ 的费用时,其价值为\ h_w\ }\)。
对于该题,价值应为节省的开销。节省的开销由两个变量决定:
\((1)\)该点的产树量;\((2)\)离该点最近的有伐木场的祖先的距离(为此要\(\color{orange}{特意加一维来解决后效性。}\))。
泛化物品则是子树;费用是子树使用的伐木场个数。
设 \(f_{x,w,z}\) 表示以x为根节点的子树,使用w个伐木场,最近祖先为z的节省开销数
那这就变成一个树形背包聊
复杂度是否为 \(O(n^2k^2)\) ..?为什么听有人说是 \(O(n^2k)\) (
[HAOI2015]树上染色(树形背包)
主要的技巧是\(\color{orange}{路径贡献转化为边贡献}\)。
考虑某一条边的贡献,可以由其一侧的黑白点个数来决定。边权可以扔点权上()
以泛化物品来理解:当给该边对应子树的黑点容量为 \(w\) ,白点容量为 \(siz_x-w\) 的时候,另一侧黑点容量为 \(k-w\) ,白点容量为 \(n+w-siz_x-k\) ,则有:
设 \(f_{x,w}\) 表示以 \(x\) 为根节点的子树(带着 \(x\) 到父亲的边)的最大贡献。
然后就是树上背包。
要分节点 \(x\) 染成黑/白的情况分别计算。
[HAOI2008] 硬币购物()
首先计算出不限制硬币个数时,也即完全背包的答案。
这个比较不变 对吧()在多组询问中,我们最先能处理的只有这个。
我本来想求购买至多/至少 \(s\) 价值的价值的东西的付款方案数,其实做不了
其实还可以求使用了至少 \(x\) 枚第 \(i\) 种硬币。使用最多 \(d_i\) 枚硬币 = 使用任意枚硬币 - 使用至少 \(d_i+1\)枚硬币。
使用至少 \(d_i+1\) 枚硬币的方案,可以是先强制选择 \(d+1\) 枚硬币,再任意选择硬币。也即 \(f_{s-c_i\times(d_i+1)}\)
可以瞎 jb 容斥。

浙公网安备 33010602011771号