10.16 考试解题报告

T1

#题目描述 :

对于给定的一个正整数n, 判断n是否能分红若干个正整数之和 (能够重复) ,
其中每一个正整数都能表示成两个质数乘积。ios

#输入描述:

第一行一个正整数 q,表示询问组数。
接下来 q 行,每行一个正整数 n,表示询问。c++

#输出描述:

q 行,每行一个正整数,为 0 或 1。0 表示不能,1 表示能。git

#样例输入:

5
1
4
5
21
25spa

#样例输出:

0
1
0
1
1code

#数据范围:

\(30\%\)的数据知足:\(q\leq20, n\leq20\)
\(60\%\)的数据知足:\(q\leq10000, n\leq5000\)
\(100\%\)的数据知足:\(q\leq10^5, n \leq 10^{18}\)ci

解题思路:

看到这个奇葩的数据范围,我先看到了\(100\%\)\(n \leq 10^{18}\)
而后我就不知道怎么想的,写了一个不知道能不能跑过60分的彻底背包?get

code:string

#include <bits/stdc++.h>
#define N 5010
#define ll long long
#define M 1010
#define _ 0

using namespace std;
map<int, int> mp; bool vis[N], b[N];
int q, m, mx, mi = 99999999, cnt, tot;
int po[10000100], f[N], qust[N << 1], prime[N];

int read() {
    int s = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
    return f ? -s : s;
}

void erlur(int s) {
    for (int i = 2; i <= s; i++) {
        if (!vis[i]) prime[++cnt] = i;
        for (int j = 1; j <= cnt; j++) {
            if (i * prime[j] > s) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}

int main() {
    freopen("a.in", "r", stdin);
//  freopen("a.out", "w", stdout);
    q = read();
    for (int i = 1; i <= q; i++) {
        qust[i] = read();
        mx = max(mx, qust[i]);
    }
    erlur(mx);
    for (int i = 1; i <= cnt; i++)
        for (int j = 1; j <= cnt; j++) {
            int an = prime[i] * prime[j];
            if (!mp[an] && an <= mx)
                mp[an] = 1, po[++tot] = an, mi = min(mi, an);
        }
    cout << tot << "\n";
    for (int i = 1; i <= q; i++) {
        if (b[qust[i]]) cout << 1 << " ";
        else {
            memset(f, 0, sizeof(f));
            int flag = 0;
            for (int j = 1; j <= tot; j++)
                for (int k = po[j]; k <= qust[i]; k++) {
                    f[k] = max(f[k], f[k - po[j]] + po[j]);
                    if (f[k] == qust[i]) flag = 1;
                }
            if (flag) b[qust[i]] = 1, puts("1");
            else puts("0");
        }
    }
    return 0;
}

在打出来的时候尝试一下\(60\%\)的范围,结果发现本身的程序跑步过去,而后就把前\(1000\)个输了出来,而后发现除了前边的\(1,2,3,5,7,11\)以外全部的数都是1,只有这两个是0.it

证实啊

\(n \leq 20\) 时, 只有\(1,2,3,5,7,11\)无解,其他均有解。
\(n > 20\) 时, 由于\(n = (n - 4) + 4 = (n-4) + 2 \ast 2\),而\((n-4)\)这个数\(\geq16\), 是必定有解的。
因此,咱们证实了对于任意的正整数\(n\),只有\(n = 1,2,3,5,7,11\)时无解,其他均有解。io

正解:

#include <bits/stdc++.h>
#define N 100010
#define M 1010
#define _ 0

using namespace std;
long long q, n;

int read() {
    int s = 0, f = 0; char ch = getchar();
    while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
    return f ? -s : s;
}

int main() {
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    q = read();
    for (int i = 1; i <= q; i++) {
        n = read();
        if (n == 1 || n == 2 || n == 3 || n == 5  || n == 7 || n == 11) puts("0");
        else puts("1");
    }
    return 0;
}

T2

#题目描述:

有一个长度为 n 的天然数序列 a,要求将这个序列刚好分红至少 m 个连续子
段。 每一个子段的价值为该子段的全部数的按位异或。要使全部子段的价值按位与
的结果最大,输出这个最大值。

#输入描述:

第一行一个正整数 \(q\),表示询问组数。
接下来 \(q\) 组输入,每组输入两行:
第一行两个正整数 \(n,m\)
第二行 \(n\) 个正整数,描述序列 \(a\)

#输出描述:

一行一个正整数,表示答案。

样例输入:

1
5 3
1 2 3 4 5

样例输出:

1

#数据范围:

\(20\%\)的数据:\(n\leq20\)
\(40\%\)的数据:\(n\leq100,ai < 256\)
\(60\%\)的数据:\(n\leq100\)
\(100\%\)的数据:\(1\leq q \leq12,1\leq m \leq n \leq 1000,0\leq a_i < 2^{30}\)

解题思路:

由于异或运算具备交换律且(x异或x = 0),因此区间异或和能够由两个前缀异或和异或获得。
考虑从高位到低位肯定答案的二进制位,而后考虑判断是否能分红至少\(m\)段。
如何判断是否能分红至少\(m\)段?
"能分红至少m段"与"最多能分红的段数\(\geq m\)"等价。
\(dp[i] = "a[1..i]这一段数最多能分红多少段"\)
转移就是直接枚举上一段的末尾\(j\),若是\([j+1,i]\)能够组成一段,那么就把\(dp[i] = max(dp[i],dp[j] + 1);\)
这样\(DP\)的复杂度是\(O(n^2)\)
总复杂度就是\(O(q \ast log(值域) \ast DP) = O(30qn^2) ≈ 3.6 * 10^8\),由于常数较小能够过。

code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1007
using namespace std;
int read() {
    char c = getchar();
    int x = 0, f = 1;
    for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for(; isdigit(c); c = getchar()) x = x * 10 + c - '0';
    return x * f;
}
int T, n, m, a[N], f[N], sum[N], ans;
int main() {
    freopen("b.in", "r", stdin);
    freopen("b.out", "w", stdout);
    T = read();
    while(T--) {
        n = read(), m = read(), sum[0] = 0, ans = 0;
        for(int i = 1; i <= n; i++) a[i] = read(), sum[i] = sum[i - 1] ^ a[i];
        for(int i = 29; i >= 0; i--) {
            f[0] = 0;
            for(int j = 1; j <= n; j++) {
                f[j] = -1;
                for(int k = 0; k < j; k++)
                    if(f[k] != -1 && ((ans | (1 << i)) & (sum[j] ^ sum[k])) == (ans | (1 << i)))
                        f[j] = max(f[j], f[k] + 1);
            }
            if(f[n] >= m) ans |= 1 << i;
        }
        printf("%d\n", ans);
    }
    return 0;
}

T3

#题目描述:

定义一个排列 \(a\) 的价值为知足\(| a[i]-i | \leq1\)\(i\) 的数量。
给出三个正整数 \(n,m,p\),求出长度为 \(n\) 且价值刚好为 \(m\) 的排列的个数对 \(p\)
模的结果。

#输入描述:

第一行两个正整数 \(T,p\)\(T\) 为数据组数,\(p\) 为模数。
接下来 \(T\) 行,每行两个正整数 \(n,m\)

#输出描述:

\(T\) 行,每行一个非负数,表示答案。

输入样例:

5 1887415157
3 1
3 2
3 3
50 10
1500 200

输出样例:

1
2
3
621655247
825984474

#数据范围:

\(10\%\)的数据:\(n \leq 10\)
\(30\%\)的数据:\(n \leq 15\)
\(50\%\)的数据:\(n\leq 200\)
另有 \(10\%\)的数据:\(m=1\)
另有 \(10\%\)的数据:\(m=n-1\)
\(100\%\)的数据:\(1 \leq T, n, m \leq 2000,2 \leq p \leq 10^{12}\)

解题思路:

由于\(n,m \leq 2000\), 并且\(p\)是事先给出的,因此咱们能够一次性预处理出\(n,m \leq 2000\)的答案。
考虑一个长度为\(i\)的排列如何变成长度为\(i+1\)的排列。
一种状况是我在它末尾加入了一个数\(i+1\),另外一种状况是我用\(i+1\)替换掉了原来排列中的一个数,而后把被换掉的数放到排列的末尾。
那么,这个排列权值的变化就是:
第一种状况:在它末尾加入了一个数\(i+1\),权值\(+1\)
第二种状况:用\(i+1\)替换掉一个数,权值 \(+=\) 加的贡献 \(-\) 换掉的数的贡献。

\(DP\)当中,咱们只须要考虑替换掉的数是不是\(i\),以及\(i\)是否在位置\(i \over (i-1)\)便可。总共有\(5\)种本质不一样的状态,分类讨论转移便可。
复杂度\(O(nm)\)

code:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N=2005;

int q,n,m,u,v;
LL p,ans,f[N][N][5],x;

int rd(){
    int re=0,f=1;char c=getchar();
    while ((c<'0')||(c>'9')) {if (c=='-') f=-f;c=getchar();}
    while ((c>='0')&&(c<='9')) {re=re*10+c-'0';c=getchar();}
    return re*f;
}

int main(){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    cin>>q>>p;
    memset(f,0,sizeof(f));
    f[1][1][0]=1ll;
    f[2][2][0]=1ll;f[2][2][1]=1ll;
    
    n=2000;
    for (int i=2;i<n;++i)
        for (int j=0;j<=n;++j){
            for (int k=0;k<=4;++k){
                x=f[i+1][j+1][0]+f[i][j][k];
                x=(x<p)?x:(x-p);
                f[i+1][j+1][0]=x;
                u=j+((k%2)==0);
                v=1+(k!=0);
                x=f[i+1][u][v]+f[i][j][k];
                x=(x<p)?x:(x-p);
                f[i+1][u][v]=x;
            }
            
            if (f[i][j][0]>0ll){
                f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][0]*(LL)(j-1))%p;
                f[i+1][j][4]=(f[i+1][j][4]+f[i][j][0]*(LL)(i-j))%p;
            }
            if (f[i][j][1]>0ll){
                x=f[i+1][j][3]+f[i][j][1];
                x=(x<p)?x:(x-p);
                f[i+1][j][3]=x;
                f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][1]*(LL)(j-2))%p;
                f[i+1][j][4]=(f[i+1][j][4]+f[i][j][1]*(LL)(i-j))%p;
            }
            if (f[i][j][2]>0ll){
                x=f[i+1][j][3]+f[i][j][2];
                x=(x<p)?x:(x-p);
                f[i+1][j][3]=x;
                f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][2]*(LL)(j-1))%p;
                f[i+1][j][4]=(f[i+1][j][4]+f[i][j][2]*(LL)(i-j-1))%p;               
            }
            if (f[i][j][3]>0ll){
                x=f[i+1][j+1][3]+f[i][j][3];
                x=(x<p)?x:(x-p);
                f[i+1][j+1][3]=x;
                f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][3]*(LL)(j-1))%p;
                f[i+1][j][4]=(f[i+1][j][4]+f[i][j][3]*(LL)(i-j-1))%p;   
            }
            if (f[i][j][4]>0ll){
                x=f[i+1][j+1][3]+f[i][j][4];
                x=(x<p)?x:(x-p);
                f[i+1][j+1][3]=x;       
                if (j>0) f[i+1][j-1][4]=(f[i+1][j-1][4]+f[i][j][4]*(LL)(j))%p;
                f[i+1][j][4]=(f[i+1][j][4]+f[i][j][4]*(LL)(i-j-2))%p;   
            }
        }
    
    for (;q>0;--q){
        cin>>n>>m;
        ans=(f[n][m][0]+f[n][m][1]+f[n][m][2]+f[n][m][3]+f[n][m][4])%p;
        cout<<ans<<'\n';
    }
    return 0;
}
相关文章
相关标签/搜索