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;
}
相關文章
相關標籤/搜索