2019.10.16CSP模拟赛总结

Problem A:

【题目描述】

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

【输入描述】

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

【输出描述】

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

【输入样例】

5
1
4
5
21
25

【输出样例】

0
1
0
1
1

【样例解释】

4=22
21=6+15=2
3+35
25=6+9+10=2
3+33+25
25=4+4+4+4+9=22+22+22+22+3*3

【数据范围】

30%的数据满足:\(q<=20,n<=20\)
60%的数据满足:\(q<=10000,n<=5000\)
100%的数据满足:\(q<=10^5,n<=10^18\)

【思路】

题意:
\(q\)次询问:能否用形如 $x = p1 * p2 \((\)p1\(,\)p2\(均为质数且可以相等) 的数相加得到给定的数\)n\(。 题解: 当\)n <= 20 \(时,只有1,2,3,5,7,11无解,其余均有解。 当\)n > 20 \(时,因为\)n = (n-4) + 4 = (n-4) + 2 * 2\(,而\)(n-4)\(这个数\)>=16\(,是一定有解的。 所以,我们证明了对于任意的正整数\)n\(, 只有\)n = 1,2,3,5,7,11\(时无解,其余均有解。 那么我们只需要在每组数据中判断一下\)n$是否等于\(1,2,3,5,7,11\)中的任意一个即可。
复杂度\(O(q)\).

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int q;
LL n;
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("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&q);
    for (;q>0;--q){
        scanf("%lld",&n);
        if ((n>3ll)&&(n!=5ll)&&(n!=7ll)&&(n!=11ll)) puts("1");else puts("0");
    }
    return 0;
}

Problen B:

【题目描述】

有一个长度为 \(n\) 的自然数序列\(a\),要求将这个序列恰好分成至少\(m\)个连续子
段。 每个子段的价值为该子段的所有数的按位异或。要使所有子段的价值按位与
的结果最大,输出这个最大值

【输入描述】

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

【输出描述】

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

【输入样例】

1
5 3
1 2 3 4 5

【输出样例】

1

【数据范围】

20%\(的数据:\)n<=20$
40%的数据:\(n<=100,ai<256\)
60%的数据:\(n<=100\)
100%的数据:\(1<=q<=12,1<=m<=n<=1000,0<=ai<2^30\)

【思路】

题意:
\(q\)组数据。
给你一个长为\(n\)的非负数序列\(a\)
定义一个区间的权值为区间的异或和。
定义一个划分的权值为分出的区间的权值的按位与的值。
要求至少把序列分成m个非空字段,求满足条件的划分的最大权值。

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

值得一提的是,直接\(O(N^3)DP\)是错误的。因为它不满足最优子结构。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int q,n,m,ans,x,s[1005],f[1005];
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 Max(int x,int y){
    return ((x>y)?x:y);
}

int main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    q=rd();
    for (;q>0;--q){
        n=rd();m=rd();
        s[0]=0;
        for (int i=1;i<=n;++i){
            x=rd();s[i]=s[i-1]^x;
        }
        ans=0;
        for (int i=29;i>=0;--i){
            memset(f,0,sizeof(f));
            f[0]=1;
            for (int j=1;j<=n;++j){
                for (int k=0;k<j;++k) if (f[k]>0){
                    x=s[j]^s[k];
                    if (((ans&x)>=ans)&&((x&(1<<i))>0))
                        f[j]=Max(f[k]+1,f[j]);
                }
            }
            if (f[n]>m) ans|=(1<<i);
        }
        
        cout<<ans<<'\n';
    }
    return 0;
}

Problem C:

【题目描述】

定义一个排列 \(a\) 的价值为满足$|a[i]-i|<=1 $的 $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<=10$
30%的数据:\(n<=15\)
50%的数据:\(n<=200\)
另有 10%的数据:\(m=1\)
另有 10%的数据:\(m=n-1\)
100%的数据:\(1<=T,n,m<=2000,2<=p<=10^12\)

【思路】

题意:
定义一个排列 $a \(的价值为满足\)|a[i]-i|<=1 $的 $i \(的数量。 给出三个正整数\) n,m,p\(,求出长度为\) n$ 且价值恰好为 \(m\) 的排列的个数对$ p$ 取模的结果。
\(T\)组询问,\(p\)事先给出。

题解:
因为\(n,m <= 2000,\)而且\(p\)是事先给出的,所以我们可以一次性预处理出\(n,m <= 2000\)的答案。

考虑一个长度为\(i\)的排列如何变成长度为\(i+1\)的排列。
一种情况是我在它末尾加入了一个数\(i+1\),另一种情况是我用\(i+1\)替换掉了原来排列中的一个数,然后把被换掉的数放到排列的末尾。
那么,这个排列权值的变化就是:
第一种情况:在它末尾加入了一个数\(i+1\),权值\(+1\)
第二种情况:用\(i+1\)替换掉一个数,\(权值 += 加的贡献 - 换掉的数的贡献。\)

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

#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;
}

猜你喜欢

转载自www.cnblogs.com/Xchu/p/11685375.html