2019 10.3模拟
1. 蒜头君的兔子
(rabbit.cpp/.in/.out 2s,256m)
蒜头君的小伙伴在 第一年 送给他一对 一岁 的兔子,并告诉他:这种兔子 刚生下来时算 0 岁
,到了 2 岁时就可以繁殖了,它在 2-10 岁时,每年会生下来一对兔子,这些兔子到了 2 岁也可
以繁殖,但这些兔子在 10 岁那年 生完仔后 不久就会死亡,蒜头君想知道,第 n 年兔
子 产仔之后(第 n 年 10 岁的兔子此时已经死亡),他会有多少对兔子。结果对
1000000007 取模。
输入格式
共一行,一个正整数 n,表示蒜头君想知道第 n 年的兔子总对。
输出格式
输出一个整数,表示第 n 年兔子总对数对 1000000007 取模的值。
数据规模
对于 30% 的数据,满足 1≤n≤103;
对于 60% 的数据,满足 1≤n≤105;
对于 100% 的数据,满足 1≤n≤109。
样例输入1
10
样例输出1
88
样例输入2
88
样例输出2
352138150
样例输入3
10086
样例输出3
405567313
考点:递推 矩阵乘法
设 f[i][j]表示第 i 年,年龄为 j 的兔子的对数。
f[i][0]=f[i-1][1]+f[i-1][2]+...+f[i-1][9]
f[i][1]=f[i-1][0]
f[i][2]=f[i-1][1]
f[i][3]=f[i-1][2]
......
f[i][9]=f[i-1][8]
f[i][10]=f[i-1][9] //已经死掉
构造初始矩阵
{ f[i][0],f[i][1],f[i][2],...,f[i][9] }*矩阵 A={ f[i+1][0],f[i+1][1],f[i+1][2],f[i+1][3],...,f[i+1][9]}
矩阵 A
0 1 0 0 0 0 0 0 0 0
1 0 1 0 0 0 0 0 0 0
1 0 0 1 0 0 0 0 0 0
1 0 0 0 1 0 0 0 0 0
1 0 0 0 0 1 0 0 0 0
1 0 0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 1 0 0
1 0 0 0 0 0 0 0 1 0
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0
矩阵乘法即可
(感谢黄学长讲解)
#include<bits/stdc++.h> using namespace std;long long f[12][12],c[12][12]; void cheng(long long a[12][12],long long b[12][12])//矩阵y乘x,结果存在y中 { memset(c,0,sizeof(c)); for(int i=1;i<=10;i++) for(int k=1;k<=10;k++) for(int j=1;j<=10;j++) c[i][j]=(c[i][j]+(a[i][k]*b[k][j])%1000000007)%1000000007; memcpy(a,c,sizeof(c)); } long long ans[12][12]; void prepare(int b)//二分快速幂求A^(n-1) { for(int i=1;i<=10;i++)ans[i][i]=1; while(b) { if(b&1)cheng(ans,f); b>>=1; cheng(f,f); } } int main() { int n; cin>>n; for(int i=2;i<=10;i++)f[i][1]=1; for(int i=2;i<=10;i++)f[i-1][i]=1; prepare(n-1); int sum=0; for(int i=1;i<=10;i++)sum=(sum+ans[2][i])%1000000007;//一开始只有一对一岁的兔子 cout<<sum%1000000007; }
2. 蒜头君的排序
(sort.cpp/.in/.out 2s,256m)
蒜头君是一个爱思考的好孩子,这一天他学习了冒泡排序,于是他就想,把一个乱序排列通过
冒泡排序排至升序需要多少次交换,这当然难不倒他,于是他想来点刺激的,给定一个 1…
n 的排列,每次从该排列中选择一个区间 [l,r],问使用冒泡排序将该区间排至升序需要多少次交换
操作。
输入格式
第一行一个整数 n,表示排列长度。
接下来一行 n 个整数,表示该排列。
接下来一行一个整数 m,表示询问次数。
接下来 m 行,每行 2 个整数 l,r,表示询问 [l,r] 区间。
输出格式
输出 m 行,每行 1 个整数,第 i 行表示第 i 个询问的答案。
数据规模
对于 30% 的数据,满足 1 ≤ n,m ≤ 300;
对于 60% 的数据,满足 1 ≤ n,m ≤ 1000;
对于 100% 的数据, 满足 1 ≤ n,m ≤ 30000, l < r, ∑∣l[i] - l[i - 1]∣ + ∑∣r[i]- r[i - 1]∣ ≤ 7 × 106。
样例输入
10
9 8 7 4 5 6 10 3 2 1
5 2
4
8 10
2 8
5 9
4 9
样例输出
3
3
13
7
9
考点:逆序对 莫队思想
如果已经求出区间 [l,r]的逆序对数,那么我们可以用类似莫队的思想快速求出 [l-
1,r],[l+1,r],[l,r+1],[l,r-1]的逆序对
如果知道区间[l , r]中的逆序对个数,那么也可以快速求出区间[l-1 , r], [l+1 , r], [l , r-1], [l ,
r+1]的逆序对个数。
③ [l-1 , r]的个数 = [l , r]的个数 + [l , r]中比 a[l-1]小的元素的个数
① [l+1 , r]的个数 = [l , r]的个数 - [l+1 , r]中比 a[l]小的元素的个数
② [l , r-1]的个数 = [l , r]的个数 - [l , r-1]中比 a[r]大的元素的个数
① [l , r+1]的个数 = [l , r]的个数 + [l , r]中比 a[r+1]大的元素的个数
Lt,Rt 表示处理完当前询问后,讨论指针所指向的区间边界。
那么对于新的一个询问[x,y]
1.Rt<y
while(Rt<y)
Rt++;
ans+=getSum(n)-getSum(A[Rt]-1)//统计之前出现过的,比 A[Rt]要大的数字的个数,即
A[Rt]贡献的逆序对数。
modify(Rt,1) //将 A[Rt]加入树状数组
2.Rt>y
while(Rt>y)
modify(A[Rt],-1) //在添加 A[Rt]进入树状数组之前, [Rt,n]之间的数都与
A[Rt]构成逆序对
ans-=getSum(n)-getSum(A[Rt]-1)//将 A[Rt]贡献的逆序对从答案中减掉
Rt--
3.Lt<x
while(Lt<x)
modify(A[Lt],-1) //在添加 A[Lt]进入树状数组前, [1,Lt]区间的数字都比 A[Lt]
要小,它们都与后加入的 A[Lt]构成逆序对。
ans-=getSum(A[Lt]-1)
Lt++
4.Lt>x
while(Lt>x) Lt-- //在区间[1,Lt-1]的数字,都与 A[Lt]构成逆序对。即之前出现过的比 A[Lt]小的数字
ans+=getSum(A[Lt]-1)
modify(A[Lt],1)
3. 蒜头君救人
(rescue.cpp/.in/.out 2s,512m)
蒜头君是一个乐于助人的好孩子,这天他所在的乡村发生了洪水,有多名村民被困于孤岛
上,于是蒜头君决定去背他们离开困境,假设蒜头君所在的村子是 n × m 的网格,网格中.
号代表平地, #号代表该地已被洪水淹没, A、 B ……等大写字母表示该地有村民被困, s 代表
蒜头君的起点, t 代表蒜头君的终点。
蒜头君的初始速度为 k 秒一格,他每次可以向上下左右 4 个方向中的一个移动 1 格。在背上
一个村民后,他的速度可能会降低,也可能会加快,但他的速度不能快于 1 秒每格,那么蒜头君
想知道,他最快需要多长时间将所有村民救出?
注意:不能在终点以外的地方放下村民;可以同时背多个村民。
输入格式
第一行 3 个正整数 n,m,k,分别表示村庄长度、宽度、蒜头君初始速度。
接下来 n 行,每行一个长度为 m 的字符串,表示村庄的地形,字符串意义如上所述。
接下来若干行,每行一个大写字母、一个整数,表示该编号的村民会使 k 增加 / 减少多少。行数等
同于地形中大写字母的个数。大写字母按字典序,即 A、 B、 C 的顺序排列,保证前后两行的字母是
连续的。
输出格式
输出 1 个整数,表示最小用时。
数据规模
对于 10% 的数据,满足 1 ≤ n,m ≤ 5,村民个数为 1;
对于 50% 的数据,满足 1 ≤ n,m ≤ 5,村民个数小于等于 5;
对于 100% 的数据,满足 1 ≤ n,m ≤ 10,村民个数小于等于 10。
样例输入
4 4 2
s.##
..A#
.B##
...t
A -3
B 4
样例输出
17
考点:状压 DP floyd
状态 f[S1][S2][p]表示背着的村民集合 S1,已到终点的村民集合 S2,蒜头君处在 p 位置(p 只
取初始状态下村民的位置、终点位置),那么显然有状态转移:
接村民:f[s1|(1<<y-1)][S2][y] = min{ dis(x,y)*v[s1]+f[s1][s2][x]}
(x∉S2,x∈S1,y∉S2,y∉S1)
终点处放下村民:f[s1^T][S2|T][end]=min(f[S1^T][S2|T][end],f[S1][S2][end])
(T 为子集,T∈S1,end 为终点)
因为在这样的定义下,S1 与 S2 是不能有交集的,这样显然不可能的情况在循环里判断一下
就可以了,实际上可能合法的情况只有 3^cnt 种。
再加上 x,y 与两个集合间的关系,循环执行的次数不会特别多