9.27Am小结
T1
问题 A: 自然数拆分
题目描述
给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复。
求拆分的方案数 mod 2147483648的结果。
输入
一个自然数N。
输出
一个整数,表示结果。
样例输入
7
样例输出
14
提示
1≤N≤4000
可行性完全背包版
首先在一定的重量中取出不同的物品就是背包
又因为数可以重复,所以是完全背包
dp[0] = 1;
for(int i = 1 ; i <= n ; ++i)
for(int j = i ; j <= n ; ++j)
dp[j] = (dp[j - i] + dp[j]) % p;
T2
问题 B: 反素数ant
题目描述
对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。如果某个正整数x满足:g(x)>g(i) 0<i<x ,则称x为反质数。例如,整数1,2,4,6等都是反质数。现在给定一个数N,你能求出不超过N的最大的反质数么?
输入
一个数N(1<=N<=2,000,000,000)。
输出
不超过N的最大的反质数。
样例输入
1000
样例输出
840
考虑因数公式:f(a) 为a的因数个数
若
a
=
∑
p
j
i
j
那
么
f
(
a
)
=
∏
i
j
+
1
若a = \sum p_j ^ {i_j} 那么f(a) = \prod i_j + 1
若a=∑pjij那么f(a)=∏ij+1
然后以递归形式组织一个遍历(可能算搜索?)
#include<bits/stdc++.h>
using namespace std;
typedef long long xt;
int pri[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
int use[10086];
int n;
xt ans;
xt maxn;
void dfs(xt k, xt num, xt tot)
{
if(tot > maxn || maxn == tot && num < ans)
{
maxn = tot;
ans = num;
}
use[k] = 0;
while(num * pri[k] <= n && use[k] + 1 <= use[k - 1])
{
++use[k];
num *= pri[k];
dfs(k + 1, num, tot * (use[k] + 1));
}
}
int main()
{
scanf("%d", &n);
use[0] = 1 << 30;
dfs(1, 1, 1);
printf("%lld", ans);
return 0;
}
T3
问题 C: 越狱
题目描述
监狱有连续编号为 1 到 n 的 n 个房间,每个房间关押一个犯人。有 m 种宗教,每个犯人可能信仰其中一种。如果相邻房间的犯人信仰的宗教相同,就可能发生越狱。求有多少种状态可能发生越狱。
输入
输入两个整数 m 和 n。
输出
可能越狱的状态数,对 100003 取余。
样例输入
2 3
样例输出
6
提示
【样例说明】
所有可能的 6 种状态为:{0,0,0},{0,0,1},{0,1,1},{1,0,0},{1,1,0},{1,1,1}。
【数据范围与提示】
对于全部数据,1≤m≤108 ,1≤n≤1012 。
水题,考虑不可能越狱的情况
当第一个位置放了一个颜色,后面就不能再选这个了
即是 m * ((m - 1) ^ (n - 1))
再用全集减去它即可
printf("%lld", ((qpow(m, n) - m * qpow(m - 1, n - 1)) % p + p) % p);
T4
我是矩阵爱好者
——我
问题 D: 佳佳的 Fibonacci
题目描述
佳佳对数学,尤其对数列十分感兴趣。在研究完 Fibonacci 数列后,他创造出许多稀奇古怪的数列。例如用 S(n) 表示 Fibonacci 前 n 项和 mod m 的值,即 S(n)=(F1 +F2 +…+Fn ) mod m,其中 F1 =F2 =1,Fi =Fi−1 +Fi−2 。可这对佳佳来说还是小菜一碟。
终于,她找到了一个自己解决不了的问题。用 T(n)=(F1 +2F2 +3F3 +…+nFn ) mod m 表示 Fibonacci 数列前 n 项变形后的和 mod m 的值。
现在佳佳告诉你了一个 n 和 m,请求出 T(n) 的值。
输入
输入数据包括一行,两个用空格隔开的整数 n,m。
输出
仅一行,T(n) 的值。
样例输入
5 5
样例输出
1
提示
【样例解释】
T(5)=(1+2×1+3×2+4×3+5×5) mod 5=1
【数据范围与提示】
对于 30% 的数据,1≤n≤1000;
对于 60% 的数据,1≤m≤1000;
对于 100% 的数据,1≤n,m≤2^31 −1。
不了解的可以先了解一下:
矩阵加速递推
首先考虑初始向量关键部分:答案信息
fib老三样: f1, f2, ans;
但问题来了,怎么将f2乘以对应的倍数呢?
首先,向量中的变量不可以互相乘起来的,只能乘以若干倍相加
怎么办?
考虑将求和公式中每一项展开:
1
+1+1
+2+2+2
+3+3+3+3
+5+5+5+5+5
+8+8+8+8+8+8
那么,由于每一层的内部元素都是fib,所以每一层的内部元素都是上一层的加上上层的元素
所以直接维护每一层的和:F
考虑把上一层中每一个元素加上一个上上层的元素(并且添到那么多个)
即:
Fi = F(i - 1) + F(i - 2) + f(i - 1) + 2 * f(i - 2)
//EIN
#include<bits/stdc++.h>
using namespace std;
typedef long long int xt;
xt m;
struct matix{
xt e[8][8];
void clr() {
memset(e, 0, sizeof e);
}
void fx() {
for(int i = 1 ; i <= 7 ; ++i)
for(int j = 1 ; j <= 7 ; ++j)
e[i][j] %= m;
}
matix():e() {}
void prt() {
for(int i = 1 ; i <= 7 ; ++i)
{
for(int j = 1 ; j <= 7 ; ++j)
printf("%d ", e[i][j]);
printf("\n");
}
}
};
//describe a.i
xt n;
//tools
matix I;
matix operator*(const matix &a, const matix &b)
{
matix c;
for(int i = 1 ; i <= 7; ++i)
for(int j = 1 ; j <= 7; ++j)
for(int k = 1 ; k <= 7 ; ++k)
c.e[i][j] += a.e[i][k] * b.e[k][j],
c.e[i][j] %= m;
return c;
}
matix qpow(matix a, xt n)
{
matix c(I);
while(n)
{
if(n & 1) c = c * a;
a = a * a;
n >>= 1;
}
return c;
}
int main()
{
for(int i = 1 ; i <= 7 ; ++i)
I.e[i][i] = 1;
scanf("%lld%lld", &n, &m);
matix A;
matix tran;
A.e[1][1] = 1;
A.e[1][2] = 1;
A.e[1][3] = 1;
A.e[1][4] = 2;
A.e[1][5] = 1;
tran.e[1][1] = 0;
tran.e[1][2] = 1;
tran.e[1][3] = 0;
tran.e[1][4] = 2;
tran.e[1][5] = 0;
tran.e[2][1] = 1;
tran.e[2][2] = 1;
tran.e[2][3] = 0;
tran.e[2][4] = 1;
tran.e[2][5] = 0;
tran.e[3][1] = 0;
tran.e[3][2] = 0;
tran.e[3][3] = 0;
tran.e[3][4] = 1;
tran.e[3][5] = 0;
tran.e[4][1] = 0;
tran.e[4][2] = 0;
tran.e[4][3] = 1;
tran.e[4][4] = 1;
tran.e[4][5] = 1;
tran.e[5][1] = 0;
tran.e[5][2] = 0;
tran.e[5][3] = 0;
tran.e[5][4] = 0;
tran.e[5][5] = 1;
printf("%lld", (A * qpow(tran, n - 1)).e[1][5]);
}