9.27Am小结

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=pjijf(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]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值