「解题报告」2023-10-31 模拟赛
A
(a.pas/c/cpp)
【题目描述】
对于给定的一个正整数 \(n\),判断 \(n\) 是否能分成若干个正整数之和(可以重复),其中每个正整数都能表示成两个质数乘积。
【输入描述】
第一行一个正整数 \(q\),表示询问组数。
接下来 \(q\) 行,每行一个正整数 \(n\),表示询问。
【输出描述】
\(q\) 行,每行一个正整数,为 \(0\) 或 \(1\)。\(0\) 表示不能,\(1\) 表示能。
【输入样例】
5
1
4
5
21
25
【输出样例】
0
1
0
1
1
【样例解释】
【数据范围】
\(30\%\) 的数据满足:\(q \le 20,n \le 20\)
\(60\%\) 的数据满足:\(q \le 10000,n \le 5000\)
\(100\%\) 的数据满足:\(q \le 10^5,n \le 10^18\)
最小的偶和数是 \(4\),最小的奇和数是 \(9\),所以,\(n \ge 13\) 是一定有解。
\(n < 13\) 时,当 \(n = 1\) 或 \(n\) 为质数时,\(n\) 不能被分解。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define orz puts("sym, cjx, gjh AK IOI!");
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? -x : x;
}
int q;
ll n;
bool check(ll n) {
if (n == 1 || n == 2 || n == 3 || n == 5 || n == 7 || n == 11) {
return 1;
}
return 0;
}
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
q = read<int>();
while (q --) {
n = read<ll>();
if (check(n)) {
puts("0");
} else {
puts("1");
}
}
return 0;
}
B
(b.pas/c/cpp)
【题目描述】
有一个长度为 \(n\) 的自然数序列 \(a\),要求将这个序列恰好分成至少 \(m\) 个连续子段。每个子段的价值为该子段的所有数的按位异或。要使所有子段的价值按位与
的结果最大,输出这个最大值。
【输入描述】
第一行一个正整数 \(q\),表示询问组数。
接下来 \(q\) 组输入,每组输入两行:
第一行两个正整数 \(n,m\)。
第二行 \(n\) 个正整数,描述序列 \(a\)。
【输出描述】
一行一个正整数,表示答案。
【输入样例】
1
5 3
1 2 3 4 5
【输出样例】
1
【数据范围】
\(20\%\) 的数据:\(n \le 20\)
\(40\%\) 的数据:\(n \le 100, a_i < 256\)
\(60\%\) 的数据:\(n \le 100\)
\(100\%\) 的数据:\(1 \le q \le 12, 1 \le m \le n \le 1000,0 \le a_i < 2^30\)
贪心的想,二进制下,高位为 \(1\),那么这个数肯定大,且 \((1000)_2 > (0100)_2\) 的,所以我们贪心地让高位在经过运算后为 \(1\),,然后从高到低依次枚举每个数位,判断该数位为 \(1\) 时能否分成 \(\ge m\) 的连续子段。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define orz puts("sym, cjx, gjh AK IOI!");
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? -x : x;
}
const int N = 1010;
int n, m;
int siz[N][31], pd[32];
ll a[N];
void solve() {
n = read<int>(), m = read<int>();
for (int i = 1; i <= n; ++ i) {
a[i] = read<ll>();
}
memset(pd, 0, sizeof pd);
memset(siz, 0, sizeof siz);
for (int i = 1; i <= n; ++ i) {
int js = 0;
ll tmp = a[i];
while (tmp) {
siz[i][js] += (tmp & 1);
++ js;
tmp >>= 1;
}
for (int j = 0; j <= 30; ++ j) {
siz[i][j] += siz[i - 1][j];
}
}
int maxx = -1;
for (int i = 30; i >= 0; -- i) {
if (siz[n][i] >= m) {
maxx = i;
break ;
}
}
if (maxx == -1) {
puts("0");
return ;
}
ll ans = 0;
pd[maxx] = 1;
for (int i = maxx; i >= 0; -- i) {
if ((siz[n][i] & 1) != (siz[n][maxx] & 1)) continue ;
int sum = 0, las = 0, cnt = 0;
for (int j = 1; j <= n; ++ j) {
if (siz[j][i] - siz[j - 1][i]) {
++ sum;
}
if (sum & 1) {
bool fg = 1;
for (int k = i + 1; k <= maxx; ++ k) {
if (!pd[k]) continue ;
if (((siz[j][k] - siz[las][k]) & 1) == 0) {
fg = 0;
break ;
}
}
if (fg) {
sum = 0;
las = j;
++ cnt;
}
}
}
if (cnt >= m) {
pd[i] = 1;
ans |= (1ll << i);
}
}
printf("%lld\n", ans);
}
int main() {
int T = read<int>();
while (T --) {
solve();
}
return 0;
}
C
(c.pas/c/cpp)
【题目描述】
定义一个排列 \(a\) 的价值为满足 \(|a[i]-i| \le 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 \le 10\)
\(30\%\) 的数据:\(n \le 15\)
\(50\%\) 的数据:\(n \le 200\)
另有 \(10\%\) 的数据:\(m=1\)
另有 \(10\%\) 的数据:\(m=n-1\)
\(100\%\) 的数据:\(1 \le T,n,m \le 2000,2 \le p \le 10^{12}\)
因为 \(n,m <= 2000\),而且 \(p\) 是事先给出的,所以我们可以一次性预处理出 \(n,m \le 2000\) 的答案。
考虑一个长度为 \(i\) 的排列如何变成长度为 \(i+1\) 的排列。
一种情况是我在它末尾加入了一个数 \(i+1\),另一种情况是我用 \(i+1\) 替换掉了原来排列中的一个数,然后把被换掉的数放到排列的末尾。
那么,这个排列权值的变化就是:
第一种情况:在它末尾加入了一个数 \(i+1\),权值 \(+1\)。
第二种情况:用 \(i+1\) 替换掉一个数,权值 \(+=\) 加的贡献 \(-\) 换掉的数的贡献。
在 DP 当中,我们只需要考虑替换掉的数是否是 \(i\),以及 \(i\) 是否在位置 \(\dfrac{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;
}