Nowcoder | [题解-N165]牛客网NOIP赛前集训营-普及组(第二场)
啊...表示一大早还没睡醒就开始打比赛(开始前一分钟的我还在桌子上趴着休眠)...表示题目思路清奇(尤其C题)...但是我还是太蒻了...\(D\)题暴力都没打...题解正式开始之前先\(\%\)一下\(\color{#FF6161}{风浔凌}\)巨佬\(qwq\)...\(320\)真是太强了\(\%\%\%\)...
\(A\) 你好诶加币
题目描述:就是简单的\(a+b\)啊(逃)
题目本质:数据范围是\(long\ long\)的\(a+b\)啊(逃)
思路:显然不可能直接算啊...所以并不可解(逃)
详解:非常显然爆\(long\ long\)的情况只有在\(a,b\)同号时才会出现,所以考虑按照\(a,b\)的符号来分类
- \(a=0\ or\ b=0:\)此时\(a+b\)显然在\(long\ long\)范围内,直接输出\(a+b\)即可
- \(a>0\ and\ b>0:\)此时有可能会爆\(long\ long\),但是又不能直接求和判断
(所以就说不可解啊(逃)),此时可以间接判断,\(\because a,b\in[1,INF]\) \(\therefore INF-b\in[0,INF-1]\) \(\because a+b>INF\Leftrightarrow a>INF-b\) \(\therefore\)在\(long\ long\)范围内可以判断\(a+b\)是否爆\(long\ long\),若不爆\(long\ long\)则输出\(a+b\),否则输出\("hello, \%lld\backslash n"\) - \(a<0\ and\ b<0:\)同理,间接判断\(a+b\)是否爆\(long\ long\),若不爆\(long\ long\)则输出\(a+b\),否则输出\("hello, \%lld\backslash n"\)
- \((a>0\ and\ b<0)or(a<0\ and\ b>0):\)此时不会爆\(long\ long\),直接输出\(a+b\)即可
\(AC\)代码:
#include<cstdio>//N165A
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
const long long INFZ=9223372036854775807LL,INFF=-9223372036854775808LL;
long long a,b;
int main(){
scanf("%lld%lld",&a,&b);
if(a==0||b==0){
printf("%lld\n",a+b);
return 0;
}
if(a>0&&b>0){
if(INFZ-a<b){
printf("\"hello, %%lld\\n\"\n");
return 0;
}
printf("%lld\n",a+b);
return 0;
}
if(a<0&&b<0){
if(INFF-a>b){
printf("\"hello, %%lld\\n\"\n");
return 0;
}
printf("%lld\n",a+b);
return 0;
}
printf("%lld\n",a+b);
return 0;
}
\(B\) 最后一次
题目描述:求不超过\(n(n\in[2,10^{12}])\)的最大质数
题目本质:就是求不超过\(n\)的最大质数啊(逃)
思路:看到数据范围之后显然不能直接算啊...所以并不可解(逃)
详解:考虑到质数的分布密度,可以从\(n\)开始倒序查找,复杂度大概在\(O(logn\cdot \sqrt{n})\)...(口胡)
\(AC\)代码:
#include<cstdio>//N165B
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=1e6;
bool notpr[N+6];
int pr[80000],cntpr;
long long n;
void pre(){
notpr[0]=notpr[1]=true;
for(int i=2;i<=N;i++){
if(!notpr[i]){
pr[++cntpr]=i;
}
for(int j=1;j<=cntpr&&i*pr[j]<=N;j++){
notpr[i*pr[j]]=true;
if(i%pr[j]==0){
break;
}
}
}
}
bool isprime(long long x){
if(x<2){
return false;
}
if(x==2||x==3){
return true;
}
int lmt=(int)sqrt(x);
for(int i=1;i<=cntpr&&pr[i]<=lmt;i++){
if(x%pr[i]==0){
return false;
}
}
return true;
}
int main(){
pre();
scanf("%lld",&n);
if(n==2){
printf("2\n");
return 0;
}
if(n==3){
printf("3\n");
return 0;
}
if(isprime(n)){
printf("%lld\n",n);
return 0;
}
for(;n>2;--n){
if(isprime(n)){
printf("%lld\n",n);
return 0;
}
}
return 0;
}
\(C\) 选择颜色
题目描述:求一个长度为\(n(n\in[3,10^9])\)的有序环用\(c(c\in[3,100])\)种颜色染色,相邻颜色不相同的染色方式有多少种
题目本质:小学组合数学题(逃)
思路:如果是一条链不成环就非常容易...乘法原理就能解决...然而成环嘛...并不可解(逃)
详解:从简单的情况开始考虑,如果是一条链不成环那么结果显然是\(c\cdot (c-1)^{n-1}\),而成环之后的合法方式显然比不成环时少,少的部分是链染色方式中首尾颜色相同的部分,所以可以考虑用链方式数减链成环后不合法的方式数求出环合法染色方式数,此时问题就在于求链染色方式中首尾相同的方式数,此时问题可以转化为一个由\(n\)个点组成的无自环的无向完全图中长度为\(n-1\)的路径数,此时设\(f_{ij}\)表示从\(1\)出发到达\(j\)的长为\(i\)的路径条数,此时不难发现\(f_{i\ j}=\sum_{k=1}^{c}f_{i-1\ k}-f_{i-1\ j}\),据此可以考虑用矩阵快速幂完成线性递推求\(f_{n-1\ j}\),则有\(f_1\)矩阵$$\begin{vmatrix}f_{1\ 1}\f_{1\ 2}\f_{1\ 3}\\vdots\f_{1\ c}\end{vmatrix}=\begin{vmatrix}0\1\1\\vdots\1\end{vmatrix}$$基底矩阵为$$base=\begin{vmatrix}0&1&1&\cdots&1\1&0&1&\cdots&1\1&1&0&\cdots&1\\vdots&\vdots&\vdots&\ddots&\vdots\1&1&1&\cdots&0\end{vmatrix}$$则可求出从\(1\)出发到\(1\)路径长为\(n-1\)的路径条数\(f_{n-1\ 1}=f_{1\ 1}\times base^{n-2}\),同理可得不合法方式的总数为\(c\times f_{1\ 1}\times base^{n-2}\),故答案为\(c\times [(c-1)^{n-1}-f_{1\ 1}\times base^{n-2}]\),此时采用快速幂和矩阵加速求解即可
\(AC\)代码:
#include<cstdio>//N165C
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int MOD=10007;
const int C=100;
int n,c;
int ans;
int base[C][C],f[C],repcc[C][C],repc1[C];
void pre(){
f[0]=0;
for(int i=1;i<c;i++){
f[i]=1;
}
for(int i=0;i<c;i++){
for(int j=0;j<c;j++){
if(i==j){
base[i][j]=0;
}
else{
base[i][j]=1;
}
}
}
}
void mula(){
memset(repc1,0,sizeof repc1);
for(int i=0;i<c;i++){
for(int j=0;j<c;j++){
repc1[i]+=base[i][j]*f[j];
repc1[i]%=MOD;
}
}
for(int i=0;i<c;i++){
f[i]=repc1[i];
}
}
void mulb(){
memset(repcc,0,sizeof repcc);
for(int i=0;i<c;i++){
for(int j=0;j<c;j++){
for(int k=0;k<c;k++){
repcc[i][j]+=base[i][k]*base[k][j];
repcc[i][j]%=MOD;
}
}
}
for(int i=0;i<c;i++){
for(int j=0;j<c;j++){
base[i][j]=repcc[i][j];
}
}
}
void qpow(int u){
while(u){
if(u&1){
mula();
}
mulb();
u>>=1;
}
}
long long llqpow(int base,int u){
long long rep=1;
while(u){
if(u&1){
rep*=base;
rep%=MOD;
}
base*=base;
base%=MOD;
u>>=1;
}
return rep;
}
int main(){
scanf("%d%d",&n,&c);
pre();
ans=llqpow(c-1,n-1);
qpow(n-2);
ans-=f[0];
ans%=MOD;
ans+=MOD;
ans%=MOD;
ans*=c;
ans%=MOD;
printf("%d\n",ans);
return 0;
}
\(D\) 合法括号序列\(\color{#55ACEE}{表示这题还没过...暂且咕着qwq}\)
题目描述:
题目本质:
思路:
详解:
\(AC\)代码: