A - A Math Problem
题意:
给定一个正整数
n
n
n ,计算有多少正整数
k
k
k 满足
k
k
⩽
n
k^k\leqslant n
kk⩽n 。数据范围1e18。
解法:
先确定上界,然后快速幂暴力即可。
参考代码:
#include <cstdio>
typedef long long ll;
ll qp(int n,int k){
ll res=1;
while (k){
if (k&1) res*=n;
n*=n;
k>>=1;
}
return res;
}
ll n;
int main(){
// freopen("in.txt","r",stdin);
while (~scanf("%lld",&n)){
if (n>=437893890380859375){
printf("%d\n",15);
continue;
}
int cnt=0;
for (int i=1;i<=14;++i){
if (qp(i,i)<=n) ++cnt;
}
printf("%d\n",cnt);
}
return 0;
}
D - Covering
题意:
一个
4
×
n
4\times n
4×n 的地板,用
2
n
2n
2n 个
1
×
2
1\times2
1×2 的地毯去覆盖,问有多少覆盖的方式。
解法:
设题中所求为
f
(
n
)
f(n)
f(n) ,递推关系为
f
(
n
)
=
f
(
n
−
1
)
+
5
f
(
n
−
2
)
+
f
(
n
−
3
)
−
f
(
n
−
4
)
f(n)=f(n-1)+5f(n-2)+f(n-3)-f(n-4)
f(n)=f(n−1)+5f(n−2)+f(n−3)−f(n−4) 。然后矩阵加快速幂即可。
以下为推导过程:
方法一:
dfs得到前几项,然后项数递增地爆搜找线性递推关系。
方法二:
愣推。考虑前两列,分五种情况,可以参见这篇博客。
参考代码:
#include <cstdio>
#include <cstring>
const int MOD=1e9+7;
typedef long long ll;
struct Matrix{
static const int row=4;
ll matrix[row][row];
Matrix(int x=0){
memset(matrix,0,sizeof(matrix));
for (int i=0;i<row;++i)
matrix[i][i]=x;
}
Matrix operator*(const Matrix &oth)const{
Matrix res;
for (int i=0;i<row;++i){
for (int j=0;j<row;++j){
for (int k=0;k<row;++k){
res.matrix[i][j]=
(res.matrix[i][j]+matrix[i][k]*oth.matrix[k][j]+MOD)%MOD;
}
}
}
return res;
}
};
Matrix qp(Matrix n,ll k){
Matrix res(1);
while (k){
if (k&1) res=res*n;
n=n*n;
k>>=1;
}
return res;
}
ll n;
int solve(){
if (n<=4){
if (n==1) return 1;
else if (n==2) return 5;
else if (n==3) return 11;
else return 36;
}
n-=4;
Matrix x;
x.matrix[0][0]=1;
x.matrix[0][1]=5;
x.matrix[0][2]=1;
x.matrix[0][3]=-1;
x.matrix[1][0]=1;
x.matrix[2][1]=1;
x.matrix[3][2]=1;
Matrix y=qp(x,n);
return (y.matrix[0][0]*36%MOD+
y.matrix[0][1]*11%MOD+
y.matrix[0][2]*5%MOD+
y.matrix[0][3]*1%MOD+MOD)%MOD;
}
int main(){
// freopen("in.txt","r",stdin);
while (~scanf("%lld",&n)){
printf("%d\n",solve());
}
return 0;
}
E - CS Course
题意:
给定
n
n
n 个非负整数
a
1
,
⋯
 
,
a
n
a_1,\cdots,a_n
a1,⋯,an 和
q
q
q 个正整数
p
p
p,问除了
a
p
a_p
ap 之外所有数的且、或、异或运算结果。
解法:
记录
a
i
a_i
ai 之前和之后所有数字的运算结果,则当询问到
a
i
a_i
ai 时将二者再做一次运算即可。
参考代码:
#include <cstdio>
const int MAXN=1e5+10;
int n,q;
int a[MAXN],dp[MAXN][2][3];
int main(){
// freopen("in.txt","r",stdin);
while (~scanf("%d%d",&n,&q)){
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
dp[2][0][0]=dp[2][0][1]=dp[2][0][2]=a[1];
for (int i=3;i<=n;++i){
dp[i][0][0]=dp[i-1][0][0]&a[i-1];
dp[i][0][1]=dp[i-1][0][1]|a[i-1];
dp[i][0][2]=dp[i-1][0][2]^a[i-1];
}
dp[n-1][1][0]=dp[n-1][1][1]=dp[n-1][1][2]=a[n];
for (int i=n-2;i>=1;--i){
dp[i][1][0]=dp[i+1][1][0]&a[i+1];
dp[i][1][1]=dp[i+1][1][1]|a[i+1];
dp[i][1][2]=dp[i+1][1][2]^a[i+1];
}
for (int i=0;i<q;++i){
int p;
scanf("%d",&p);
if (p==1){
printf("%d %d %d\n",dp[1][1][0],dp[1][1][1],dp[1][1][2]);
}
else if (p==n){
printf("%d %d %d\n",dp[n][0][0],dp[n][0][1],dp[n][0][2]);
}
else{
int _and=dp[p][0][0]&dp[p][1][0];
int _or=dp[p][0][1]|dp[p][1][1];
int _xor=dp[p][0][2]^dp[p][1][2];
printf("%d %d %d\n",_and,_or,_xor);
}
}
}
return 0;
}
G - Duizi and Shunzi
题意:
给定
n
n
n 个数
a
1
⋯
 
,
a
n
a_1\cdots,a_n
a1⋯,an ,定义两个相同的数字为一个“对子”,三个连续的数字为一个“顺子”。问用这
n
n
n 个数最多能组成多少个对子加顺子(使和值达到最大,一个数字只能用一次,可以不用光)。
解法:
思路:贪心,用最少的牌组成尽可能多的对子或顺子。
由于组成“顺子”需要三张,而组成“对子”只需要两张,因此优先组成对子。但也有特例除外,即sample中1 2 3 3 4 5的情况。这是因为,按照优先规则,3前面的1和2没有被使用,浪费了。如果当前考虑的这张牌能利用起它前面的两张牌,则等于是用1张就组成了一个东西,是比组对子更优的。
因此最终解法为,从小到大排好序,从前向后看,当前牌如果能和前面两张牌组成顺子,则组顺子,然后用剩下的组对子;否则全部用来组对子。
参考代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define max std::max
#define min std::min
const int MAXN=1e6+10;
int a[MAXN];
int n,lo,hi;
bool shunzi(int x){
return x>=lo&&x+2<=hi&&a[x]&&a[x+1]&&a[x+2];
}
int solve(){
int res=0;
for (int i=lo;i<=hi;++i){
if (shunzi(i-2)){
a[i-2]--;
a[i-1]--;
a[i]--;
res++;
}
res+=a[i]>>1;
a[i]=(bool)(a[i]&1);
}
return res;
}
int main(){
// freopen("in.txt","r",stdin);
while (~scanf("%d",&n)){
memset(a,0,sizeof(a));
lo=MAXN,hi=0;
for (int i=0;i<n;++i){
int x;
scanf("%d",&x);
hi=max(hi,x);
lo=min(lo,x);
a[x]++;
}
printf("%d\n",solve());
}
return 0;
}