T1:a
【题目描述】
对于给定的一个正整数(n), 判断n是否能分成若干个正整数之和 (可以重复) ,其中每个正整数都能表示成两个质数乘积。
【输入描述】
第一行一个正整数 (q),表示询问组数。
接下来 (q) 行,每行一个正整数 (n),表示询问。
【输出描述】
(q) 行,每行一个正整数,为$ 0$ 或 (1)。(0) 表示不能,(1) 表示能。
【输入样例】
(5)
(1)
(4)
(5)
(21)
(25)
【输出样例】
(0)
(1)
(0)
(1)
(1)
【样例解释】
(4=2*2)
(21=6+15=2*3+3*5)
(25=6+9+10=2*3+3*3+2*5)
(25=4+4+4+4+9=2*2+2*2+2*2+2*2+3*3)
【数据范围】
(30\%)的数据满足:(qleq 20,nleq 20)
(60\%)的数据满足:(qleq 10000,nleq 5000)
(100\%)的数据满足:(qleq 10^5,nleq 10^{18})
Solution
当(n leq 20) 时,只有(1,2,3,5,7,11)无解,其余均有解。
当(n > 20) 时,因为(n = (n-4) + 4 = (n-4) + 2 * 2),而((n-4))这个数(geq 16),是一定有解的。
所以,我们证明了对于任意的正整数(n),
只有(n = 1,2,3,5,7,11)时无解,其余均有解。
那么我们只需要在每组数据中判断一下n是否等于(1,2,3,5,7,11)中的任意一个即可。
复杂度(O(q)).
Code
//****定理
#include <cstdio>
#include <iostream>
using namespace std;
int q;
long long x;
long long read() {
long long s = 0, w = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') w = -1;ch = getchar();}
while(isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
return s * w;
}
int pd(int x) {
if(x == 4) return 1;
if(x == 6) return 1;
if(x == 8) return 1;
if(x == 9) return 1;
if(x == 10) return 1;
if(x == 12) return 1;
return 0;
}
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
q = read();
while(q--) {
x = read();
if(x <= 12)
if(pd(x)) printf("1
");
else printf("0
");
else printf("1
");
}
fclose(stdin);
fclose(stdout);
return 0;
}
T2:b
【题目描述】
有一个长度为(n)的自然数序列(a),要求将这个序列恰好分成至少(m) 个连续子段。 每个子段的价值为该子段的所有数的按位异或。要使所有子段的价值按位与的结果最大,输出这个最大值。
【输入描述】
第一行一个正整数 (q),表示询问组数。
接下来$ q$ 组输入,每组输入两行:
第一行两个正整数 (n,m)。
第二行 $n $个正整数,描述序列 (a)。
【输出描述】
一行一个正整数,表示答案。
【输入样例】
(1)
(5) (3)
(1) (2) (3) (4) (5)
【输出样例】
(1)
【数据范围】
(20\%)的数据:(nleq 20)
(40\%)的数据:(nleq 100,a_i<256)
(60\%)的数据:(nleq 100)
(100\%)的数据:(1leq qleq 12,1leq mleq nleq 1000,0leq a_i<2^{30})
Solution
因为异或运算具有交换律且((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 * log(值域) * DP) = O(30qn^2) approx 3.6 * 10^8),因为常数较小可以过。
值得一提的是,直接(O(N^3)DP)是错误的。因为它不满足最优子结构。
Code
#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<<'
';
}
return 0;
}
T3:c
【题目描述】
定义一个排列 (a) 的价值为满足(|a[i]-i|leq 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\%)的数据:(nleq 10)
(30\%)的数据:(nleq 15)
(50\%)的数据:(nleq 200)
另有 (10\%)的数据:(m=1)
另有$ 10%(的数据:)m=n-1$
(100\%)的数据:(1leq T,n,mleq 2000,2leq pleq 10^{12})
Solution:
题解:
因为(n,m leq 2000),而且(p)是事先给出的,所以我们可以一次性预处理出(n,mleq 2000)的答案。
考虑一个长度为(i)的排列如何变成长度为(i+1)的排列。
一种情况是我在它末尾加入了一个数(i+1),另一种情况是我用(i+1)替换掉了原来排列中的一个数,然后把被换掉的数放到排列的末尾。
那么,这个排列权值的变化就是:
第一种情况:在它末尾加入了一个数(i+1),权值(+1)。
第二种情况:用(i+1)替换掉一个数,权值 (+=) 加的贡献$ -$ 换掉的数的贡献。
在(DP)当中,我们只需要考虑替换掉的数是否是(i),以及(i)是否在位置(frac{i}{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<<'
';
}
return 0;
}
谢谢收看,祝身体健康!