對於給定的一個正整數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; }
有一個長度爲 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\),由於常數較小能夠過。
#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; }
定義一個排列 \(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)\)。
#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; }