zoukankan      html  css  js  c++  java
  • [比赛|考试]nowcoder NOIPpj组第二场

    nowcoder NOIPpj组第二场

    370pts/400pts(100,100,100,70) rank3

    给自己的反思:前3题都A了,T4O(N^2)不会就是不会(没准是我懒得推了),DP了70pts,没什么好反思的,反正就是保持状态吧(qtmd我就是个喜欢做水题的菜鸡)反正以后水题保证一遍AC难题保证暴力就行

    以下是题解


    题目链接

    T1.你好诶加币

    牛牛刚学习了输入输出,他遇到了一道这样的题目。

    输入2个整数a和b
    保证输入的a和b在long long范围之内,即满足
    -9223372036854775808 <= a, b <= 9223372036854775807
    计算a+b的值,即这两个数字的和。
    如果a+b在long long范围之内,即满足
    -9223372036854775808 <= a + b <= 9223372036854775807
    那么输出一行一个整数表示a+b的结果。
    如果a+b不在long long范围之内,即越界了,那么输出"hello, %lld ",包含引号。

    用python来cheatforscore是一个好方法,现在luogu,nowcoder都资磁python3了

    既然是python那么就简单了直接模拟就行,如下

    def main():
        s = input().split()
        res = int(s[0]) + int(s[1])
        if res <= 9223372036854775807 and res >= -9223372036854775808:
            print(res)
        else:
            print('''"hello, %lld\n"''')
    main()
    

    唠唠正解吧,可以用__int128(至少在nowcoder可以)(zwfymqz大佬残忍地没有开类型转换还是炸了)。打个高精就能过。懒得写代码。

    T2. 最后一次

    牛牛最近学习了质数的概念。 质数指在大于1的自然数中,除了1和它本身以外不再有其他因数。 输入一个n,输出小于等于n最大的质数。

    可以从n往下枚举并判断这个数是否为素数。

    暴力判素数可做,记得全程开long long,注意是全程

    暴力判断素数代码(17ms)

    #include <bits/stdc++.h>
    using namespace std;
     
    long long n;
     
    bool isprime(long long x)
    {
        for (long long i = 2; i * i <= x; i++)
            if (x % i == 0)
                return false;
        return true;
    }
     
    int main()
    {
        cin >> n;
        for (long long i = n; i >= 1; i--)
        {
            if (isprime(i))
            {
                cout << i << endl;
                return 0;
            }
        }
        return 0;
    }
    

    然而我的Miller-Rabin也过了(3ms),注意乘法不能直接乘会炸long long,要写龟速乘法(程序中的turtle())

    #include <bits/stdc++.h>
    using namespace std;
     
    long long turtle(long long x, long long y, long long p)
    {
        long long res = 0;
        while (y > 0)
        {
            if (y & 1)
                res = (res + x) % p;
            (x <<= 1) %= p;
            y >>= 1;
        }
        return res;
    }
     
    long long qpow(long long x, long long y, long long p)
    {
        long long res = 1;
        while (y > 0)
        {
            if (y & 1)
                res = turtle(res, x, p);
            x = turtle(x, x, p);
            y >>= 1;
        }
        return res;
    }
     
     
     
    bool Miller_Rabin(long long N, long long a)
    {
        if (N == a)
            return true;
        if (N == 46856248255981LL || N < 2 || N % a == 0)
            return false;
        long long d = N - 1;
        while (!(d & 1))
            d >>= 1;
        long long t = qpow(a, d, N);
        while (d != N - 1 && t != 1 && t != N - 1)
        {
            t = turtle(t, t, N);
            d <<= 1;
        }
        return (t == N - 1) || ((d & 1) == 1);
    }
     
    inline bool prime(long long x)
    {
        return (Miller_Rabin(x, 2) && Miller_Rabin(x, 3) && Miller_Rabin(x, 7) && Miller_Rabin(x, 61) && Miller_Rabin(x, 24251));
    }
     
     
    int main()
    {
        long long n;
        cin >> n;
        for (long long i = n; i >= 1; i--)
        {
            if (prime(i))
            {
                cout << i << endl;
                return 0;
            }
        }
        return 0;
    }
    

    T3.选择颜色

    n个人排成一个环形,每个人要从c种颜色中选择一个。 牛牛希望相邻的人选择的颜色是不同的,问有多少种方案。输出方案数对10007取模的结果。 人是有顺序的,环旋转同构算不同的方案。

    结论题。有通项公式,我不会推。这里给出递推公式的推的过程。

    考虑一个有n个节点的环,按照逆时针顺序依次给环上节点染色。
    第一个节点有c种颜色,第2~n-1个节点由于不能与他上一个节点的颜色重复,每个节点都有(c-1)种颜色。(其实这句话没用)
    考虑第n个节点,设情况数量为f(n)。如果他两边的节点(第1个和第n-1个)颜色相同,那么他有c-1种颜色。否则有c-2种颜色。
    对于两边珠子颜色相同的情况,把第(n-1)个节点和第n个节点删除,剩余n-2个节点环--第(n-2)个节点和第1个节点连接在一起。由于第(n-1)个节点的颜色和第1个节点颜色相同,所以一定是合法的。这个环有f(n-2)个节点,也就是有f(n-2)种情况,新加的珠子有c-1种,即一共有(c-1)f(n-2)种。
    对于两边珠子颜色不同的情况,把该节点(第n个节点)删除,剩余n-1个节点的环--第(n-1)个节点和第1个节点连接在一起,由于第(n-1)个节点的颜色和第1个节点颜色不相同,所以这个环也一定是合法的。这个环有f(n-1)个节点,新加的珠子有(c-2)种颜色,所以一共有(c-2)f(n-1)种颜色。

    根据加法原理和乘法原理,f(n)=(c-2)f(n-1)+(c-1)f(n-2)。(感谢@_Lancy的指正)这是一个2阶线性递推式,可以O(n)推出。如果使用矩阵快速幂可以加速到O(logn)。(原谅我用四个变量写矩阵。。。。。)

    #include <bits/stdc++.h>
    #define p 10007
    using namespace std;
     
    struct matrix
    {
        int a, b, c, d;
    };
    matrix operator*(const matrix &x, const matrix &y)
    {
        return (matrix){(x.a * y.a + x.b * y.c) % p, (x.a * y.b + x.b * y.d) % p, (x.c * y.a + x.d * y.c) % p, (x.c * y.b + x.d * y.d) % p};
    }
     
    matrix qpow(matrix x, long long y)
    {
        matrix res;
        res.a = res.d = 1;
        res.b = res.c = 0;
        while (y > 0)
        {
            if (y & 1)
                res = res * x;
            x = x * x;
            y >>= 1;
        }
        return res;
    }
     
     
    int main()
    {
        long long n, c;
        cin >> n >> c;
        matrix start3;
        start3.a = c * (c - 1) * (c - 2) % p;
        start3.c = c * (c - 1) % p;
        start3.b = 0;
        start3.d = 0;
        matrix d;
        d.a = c - 2;
        d.b = c - 1;
        d.c = 1;
        d.d = 0;
        cout << (qpow(d, n - 3) * start3).a << endl;
        return 0;
    }
    

    T4.合法括号序列

    键盘上有左括号(,右括号),和退格键-,共三个键。 牛牛希望按键n次,使得输入的字符串恰好一个合法的括号序列。 每按一次左括号(,字符串末尾追加一个左括号( 每按一次右括号),字符串末尾追加一个右括号) 每按一次退格键-,会删掉字符串的最后一个字符, 特别的,如果字符串为空,牛牛也可以按退格,但是什么都不会发生。 输出方案数对p取模,注意p可能不是质数。 只要按键方法不同,就是不同的方案,即使得到的序列一样。

    因为我很弱,所以这里只给出70pts的solution

    区间上的问题考虑dp。但是这题尝试设状态的时候发现会有后效性---你在按backspace后不知道你删除了什么。所以这题考虑倒着做:按backspace键相当于取消掉后面最近的一个非backspace字符的效果,在下面我们称之为forwardspace。(backspace的效果可以累积,自己脑洞打开想一下这是什么个操作)这样就可以设状态了。令f[i][j][k]代表后i个字符,括号深度为j,此时有k个累积的backspace的效果。我们不难很难得到状态转移如下:(以下均为填表法)

    状态 加一个左括号 加一个右括号 加一个forwardspace
    f[i][0][0] f[i-1][0][1] f[i-1][1][0],f[i-1][0][1]
    f[i][j][0] f[i-1][j-1][0],f[i-1][j][1] f[i-1][j+1][0],f[i-1][j][1]
    f[i][j][k] f[i-1][j][k+1] f[i-1][j][k+1] f[i-1][j][k-1]
    f[i][0][0] = f[i-1][0][1] + f[i-1][1][0] + f[i-1][0][1]
    f[i][j][0] = f[i-1][j-1][0] + f[i-1][j][1] + f[i-1][j+1][0] + f[i-1][j][1]
    f[i][j][k] = f[i-1][j][k+1] + f[i-1][j][k+1] + f[i-1][j][k-1]
    

    注意到当前字符串没有文本按backspace没有效果,转化一下就是最后剩余多余的forwardspace没有效果。答案是所有f[n][0][k]的和。

    然后我们开三维的1000数组,这样开空间会炸,注意到i只与i-1有关,开滚动数组即可,把第1维滚掉。

    时间复杂度:O(n^3),空间复杂度O(n^2),期望得分70pts

    我的代码如下

    #include <bits/stdc++.h>
    using namespace std;
     
    int f[2][1010][1010];
    int n, p, ans;
     
    int main()
    {
        scanf("%d%d", &n, &p);
        f[0][0][0] = 1;
        for (int i = 1, t = 1; t <= n; i ^= 1, t++)
        {
            f[i][0][0] = (f[i ^ 1][1][0] + 2 * f[i ^ 1][0][1]) % p;
            for (int j = 1; j <= n; j++)
                f[i][j][0] = (f[i ^ 1][j - 1][0] + f[i ^ 1][j + 1][0] + 2 * f[i ^ 1][j][1]) % p;
            for (int k = 1; k <= n; k++)
                for (int j = 0; j <= n; j++)
                    f[i][j][k] = (f[i ^ 1][j][k + 1] * 2 + f[i ^ 1][j][k - 1]) % p;
        }
        for (int k = 0; k <= n; k++)
            (ans += f[1 & n][0][k]) %= p;
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    phpcms后台进入地址(包含No permission resources错误)
    phpmyadmin上传大sql文件办法
    ubuntu彻底卸载mysql
    Hdoj 2602.Bone Collector 题解
    一篇看懂词向量
    Hdoj 1905.Pseudoprime numbers 题解
    The Python Challenge 谜题全解(持续更新)
    Hdoj 2289.Cup 题解
    Hdoj 2899.Strange fuction 题解
    Hdoj 2199.Can you solve this equation? 题解
  • 原文地址:https://www.cnblogs.com/oier/p/9651215.html
Copyright © 2011-2022 走看看