对于给定的一个正整数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; }