AtCoderBeginnerContest132 DEF
https://atcoder.jp/contests/abc132
D题 排列组合
题意:
给定n,k,代表n个球中有k个蓝球,n-k个红球
将这n个球排成一列,每次可以将连续的一段蓝球取走,直到所有的蓝球全部被取走
输出k个数其中i(1<=i<=k)表示取 i 次才能取走所有的蓝球的摆放球的方案数
思路:
好久没写排列组合,不管是求法还是写法都有些忘了,温故而知新啊
定义m代表篮球个数,t代表红球个数,
假设要取 i 次,t个红球代表有t+1个地方可以给蓝球放,放 i 坨
所以是C(t+1,i)
如何分成i坨呢?隔板法,分成i坨要插入i-1个板子,有m-1个位置可以插
所以是C(m-1,i-1)
所以取第i次的方案数 = C(t+1,i) * C(m-1,i-1)
求组合的函数有点忘了,没有写 if(a<b) return 0; re了一发
const int N=2010,M=1e6+10;
int t,n,m;
ll fac[N],inv[N];
ll qmi(ll a,ll b){
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll ck(ll a,ll b){
if(a < b) return 0;
return fac[a] * inv[a-b] % mod * inv[b] % mod;
}
int main(){
fac[0] = inv[0] = 1;
for(int i=1;i<N;i++){
fac[i] = fac[i-1] * i % mod;
inv[i] = inv[i-1] * qmi(i,mod-2) % mod;
}
read(n),read(m);
t = n - m;
for(int i=1;i<=m;i++){
printf("%lld\n", ck(t + 1,i) * ck(m - 1,i-1) % mod);
}
return 0;
}
E题 BFS
题意:
一个有向图,给定起点s和终点t,每次只能走三步,不限制走的次数。
问最终能否在第三步到达终点。
思路:
bfs的一个变形,定义dis[i][0~2]
dis[i][0] = 3步的倍数到 i 的 最短距离
dis[i][1] = 3步倍数多一步到 i 的最短距离
dis[i][2] = 3步倍数多两部到 i 的最短距离
int s,t,n,m;
int e[N],ne[N],h[N],idx;
int dis[N][3];
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a]=idx++;
}
int main(){
memset(h,-1,sizeof h);
memset(dis,-1,sizeof dis);
read(n),read(m);
for(int i=0;i<m;i++){
int u,v;
read(u),read(v);
add(u,v);
}
read(s),read(t);
queue<pii> q;
q.push({s,0});
dis[s][0] = 0;
while(q.size()){
auto top = q.front();q.pop();
int u = top.first;
int step = top.second;
for(int i=h[u];~i;i=ne[i]){
int v = e[i];
int nowstep = (step + 1) % 3;
if(dis[v][nowstep] != -1) continue;
dis[v][nowstep] = dis[u][step] + 1;
q.push({v,nowstep});
}
}
if(dis[t][0]==-1) puts("-1");
else printf("%d\n", dis[t][0] / 3);
return 0;
}
F题 分块 + dp
题意:
给定一个N,K,问生成长度为K的数组,且相邻两数的乘积不超过N的方案数。
思路:
dp ,
f [ i ] [ j ] 代表生成长度为i 的数组,且最后一位是 j 的方案数
i 的 范围是 1 - 100
j 的 范围是 1 - 1e9
巨大无比!
1e9考虑分块,就是100 * sqrt(1e9) 不超时
怎么搞
将数字分成s,b两部分,s代表小于等于sqrt(n)的数,b代表大于sqrt(n)的数
前sqrt(n)个数,也就是s里的数,怎么乘都不会超时,任意两个相乘都<=n,
将 b 里的数分成 sqrt(n) 块, 第 i 块 里的数 * i <= n,且这个i是他能乘的最大的数了。
比如考虑第1块,
其实所有b里的数都满足, 乘 1 <= n,
但是有些小一些的数满足乘2 <= n,那它就属于第2块。
可以知道 第 i 块里数的数量 = n / i - n / ( i + 1 );
定义
s[ i ][ j ]代表长度为i的序列,最后一个数是j的方案数,就类似于上面的f[i][j]
b[ i ][ j ]代表长度为i的序列,最后一个数属于第 j 块的方案数
那答案其实就是
r
e
s
=
∑
i
=
1
n
(
s
[
k
]
[
i
]
+
b
[
k
]
[
i
]
)
res = \sum_{i=1}^{\sqrt{n}} (s[k][i]+b[k][i])
res=∑i=1n(s[k][i]+b[k][i])
状态转移方程:
s
[
i
]
[
j
]
=
∑
r
=
1
n
s
[
i
−
1
]
[
r
]
+
∑
r
=
j
n
s
[
i
−
1
]
[
r
]
s[i][j] = \sum_{r=1}^{\sqrt{n}} s[i-1][r] + \sum_{r=j}^{\sqrt{n}} s[i-1][r]
s[i][j]=∑r=1ns[i−1][r]+∑r=jns[i−1][r]
s里任意两数相乘都满足,b里是第j块及之后的数才能满足
b
[
i
]
[
j
]
=
(
n
i
−
n
i
+
1
)
∗
∑
r
=
1
j
s
[
i
−
1
]
[
r
]
b[i][j] = (\frac{n}{i} - \frac{n}{i+1} ) * \sum_{r=1}^{j} s[i-1][r]
b[i][j]=(in−i+1n)∗∑r=1js[i−1][r]
第j块里的个数 * 前j个数
注意:如果n是平方数,则s[i][sqrt(n)]和b[i][sqrt(n)]等价,要去掉一个。
const int N=110,M=4e4+10;
const int mod = 1e9+7;
ll t,n,m;
ll s[N][M],b[N][M];
int main(){
cin>>n>>m;
ll tn = sqrt(n);
s[0][1] = 1;//预处理
for(int i=1;i<=m;i++){
for(int j=1;j<=tn;j++){
s[i-1][j] = (s[i-1][j] + s[i-1][j-1]) % mod;
b[i-1][j] = (b[i-1][j] + b[i-1][j-1]) % mod;
}
for(int j=1;j<=tn;j++){
s[i][j] = (s[i-1][tn] + ((b[i-1][tn] - b[i-1][j-1]) % mod + mod) % mod) % mod;
b[i][j] = (n/j - n/(j+1)) * s[i-1][j];
if(j == n/j) b[i][j] = 0;
}
}
ll res = 0;
for(int i=1;i<=tn;i++){
res = (res + (s[m][i] + b[m][i]) % mod) % mod;
}
printf("%lld\n", res);
return 0;
}