题目链接:https://ac.nowcoder.com/acm/contest/549#question
先贴一波官方题解
A-小A的签到题
显然复制代码并不能AC,实际上要求的是f2n−fn−1fn+1fn2−fn−1fn+1,其中fnfn是斐波那契数列的第n项,简单观察或推导可以得出结论:∀n≥3∀n≥3, 若为奇数则为-1,否则就为1。
复杂度:O(1)O(1)
B-小A的回文串
求n个串的最大回文子串的长度的最大值。枚举每一个变化后的字符串,对每个串跑一遍马拉车即可。
复杂度:O(n2)O(n2)
C-小A买彩票
考虑买n张彩票的总的方案数是4n4n,然后统计不亏本的方案数,记录f[i][j]f[i][j]是买到第i张彩票总获利为j的总方案数。f[i][j]=∑f[i−1][j−k],k=1,2,3,4f[i][j]=∑f[i−1][j−k],k=1,2,3,4 ,最后统计一下不亏本的方案数即可。由于数据规模很小,考虑分别组合枚举有多少个1,2,3,4也可以通过。
复杂度:O(n2)O(n2)
D-小A的位运算
预处理了一下前缀和后缀,然后枚举那个不选的数就可以了。
复杂度:O(n)O(n)
E-小A的路径
矩阵表示第一天的时候u到v有多少条路径,然后直接做矩阵k次幂就能得到k天从u到v的路径数,最后统计一下就可以了。
复杂度:O(n3logk)O(n3logk)
F-小A的最短路
这张图可以认为是边权全为1的树上增加了一条边权为0的边。
首先先不考虑多出来的一条边,那么dep[u]表示点u的深度,任意两点u,v的最短的距离就是dep[u]+dep[v]−2dep[lca(u,v)]dep[u]+dep[v]−2dep[lca(u,v)],加上这条边后另外一种可能最短的路径就是经过这条边权为0的边,可以建图之后跑一次最短路,每次查询的答案都是min(dis[u]+dis[v],dep[u]+dep[v]−2dep[lca(u,v)])min(dis[u]+dis[v],dep[u]+dep[v]−2dep[lca(u,v)])。
复杂度:O((n+q)logn)O((n+q)logn)
G-小A和小B
这题由于疏忽,一开始的时候题意上误导了大家,在这里向大家道歉。
双向BFS,只要搜索的时候判断当前走过的位置是否被对方走过就可以了。
复杂度:O(nm)O(nm)
H-小A的柱状图
单调栈的模板题,考虑每个位置向两边拓展的最左边的边界和最右边的边界L[i],R[i]L[i],R[i],分别从左到右和从右到左用一个单调栈维护。处理一下底边的前缀和,扫一遍就可以求出最大值。
复杂度:O(n)O(n)
I-小A取石子
判断一下小A是否能够取石子,当且仅当小A原本就是必败态并且不能取出任何石子的情形下,小A会输否则小A都会赢。
复杂度:O(n)O(n)
J-小A的数学题
有很多种做法,一种比较常规的做法是:
∑ni=1∑mj=1gcd(i,j)2∑i=1n∑j=1mgcd(i,j)2
=∑min(n,m)d=1d2(∑ni=1∑mj=1gcd(i,j)=d)∑d=1min(n,m)d2(∑i=1n∑j=1mgcd(i,j)=d)
记f(d)=(∑ni=1∑mj=1gcd(i,j)=d)记f(d)=(∑i=1n∑j=1mgcd(i,j)=d)
有F(t)=∑d|tf(d)=[nd][md]有F(t)=∑d|tf(d)=[nd][md]
有f(d)=∑d|tμ(td)F(t)=∑d|tμ(td)[nd][md]有f(d)=∑d|tμ(td)F(t)=∑d|tμ(td)[nd][md]
处理因子直接计算复杂度O(nlogn)O(nlogn)就可以通过本题
A标程略
B:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519506
C:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519522
D:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519534
E:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519540
F:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519544
G:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519557
H:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519566
I:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519576
J:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40519587
A.小A的签到题
链接:https://ac.nowcoder.com/acm/contest/549/A
来源:牛客网
题目描述
这是一道签到题,主要考验比赛时的手速。
接下来是一段很简单的代码,或许你提交它就可以AC。
#include<bits/stdc++.h>
using namespace std;
int main() {
long long n;
scanf("%lld",&n);
long long f1=1,f2=1,f3;
for(int i=3;i<=n;i++) {
f3=f1+f2;
f1=f2;
f2=f3;
}
printf("%lld\n",f3*f3-f1*f1-f1*f3);
return 0;
}
输入描述:
一行一个整数N一行一个整数N
输出描述:
一行一个整数表示结果一行一个整数表示结果
示例1
输入
复制
3
输出
复制
1
备注:
3≤N≤1e11
说是签到题,其实是找规律题,10^11的数据,O(n)也会超时,同时我们也要考虑下会不会数据溢出,所以再枚举1-10的时候,我们可以找到规律 奇数为1,偶数为-1,考心态的规律题?
#include<bits/stdc++.h>
using namespace std;
int main() {
long long n;
scanf("%lld",&n);
if(n%2==1)
printf("1\n");
else
printf("-1\n");
return 0;
}
B.小A的回文串
链接:https://ac.nowcoder.com/acm/contest/549/B
来源:牛客网
题目描述
小A非常喜欢回文串,当然我们都知道回文串这种情况是非常特殊的。所以小A只想知道给定的一个字符串的最大回文子串是多少,但是小A对这个结果并不是非常满意。现在小A可以对这个字符串做一些改动,他可以把这个字符串最前面的某一段连续的字符(不改变顺序)移动到原先字符串的末尾。那么请问小A通过这样的操作之后(也可以选择不移动)能够得到最大回文子串的长度是多少。
输入描述:
一行一个字符串表示给定的字符串S一行一个字符串表示给定的字符串S
输出描述:
一行输出一个整数,表示通过这样的操作后可以得到最大回文子串的长度。
示例1
输入
复制
dcbaabc
输出
复制
7
说明
将前面的dcba移动到末尾变成abcdcba,这个字符串的最大回文子串就是它本身,长度为7
备注:
N表示字符串的长度,1≤N≤5000
一个有点改动的求最长回文子串的题,有些大佬将它化为环形来做,这样就降低了一层O(n)枚举
数据貌似有点弱,先来一发暴力,前2个for枚举起点和终点,最后1个for匹配
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main(){
ios::sync_with_stdio(false);
string s;
cin>>s;
s = s.append(s);
//cout<<s<<endl;
int len = s.size()/2;
int ans=0;
for(int i=0;i<len;i++)
for(int j=len-1+i;j>=i;j--){//前2个for枚举起点和终点,最后1个for匹配
int flag = 1;
for(int k=i,p=j;k<=p;k++,p--)
if(s[k]!=s[p]){
flag = 0;
break;
}
if(flag)
ans = max(ans,j-i+1);
}
cout<<ans<<endl;
return 0;
}
C.小A买彩票
链接:https://ac.nowcoder.com/acm/contest/549/C
来源:牛客网
题目描述
小A最近开始沉迷买彩票,并且希望能够通过买彩票发家致富。已知购买一张彩票需要3元,而彩票中奖的金额分别为1,2,3,4元,并且比较独特的是这个彩票中奖的各种金额都是等可能的。现在小A连续购买了n张彩票,他希望你能够告诉他至少能够不亏本的概率是多少。
输入描述:
一行一个整数N,为小A购买的彩票数量一行一个整数N,为小A购买的彩票数量
输出描述:
输出一个最简分数a/b,表示小A不亏本的概率。若概率为1,则输出1/1,概率为0,则输出0/1。输出一个最简分数a/b,表示小A不亏本的概率。若概率为1,则输出1/1,概率为0,则输出0/1。
示例1
输入
复制
2
输出
复制
3/8
备注:
0≤n≤30
这题和某人傻乎乎的再找规律,从n=1枚举到5,然鹅答案是dp
考虑买n张彩票的总的方案数是4n4n,然后统计不亏本的方案数,记录f[i][j]f[i][j]是买到第i张彩票总获利为j的总方案数。f[i][j]=∑f[i−1][j−k],k=1,2,3,4f[i][j]=∑f[i−1][j−k],k=1,2,3,4 ,最后统计一下不亏本的方案数即可。由于数据规模很小,考虑分别组合枚举有多少个1,2,3,4也可以通过。
典型的递归公式qaq,相当于取第i张有多少种可能价值,然后累加...
有毒 W了好久,发现 dp[][]要longlong
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=35;
ll dp[N][4*N];
ll mpow(ll a,ll n){
ll ans = 1;
while(n>0){
if(n%2==1)
ans*=a;
a*=a;
n>>=1;
}
return ans;
}
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main(){
int n;
ll ans=0;
scanf("%d",&n);
dp[0][0] = 1;
for(int i=1;i<=n;i++)
for(int j=i;j<=4*i;j++)
for(int k=1;k<=4;k++)
if(j-k>=0) dp[i][j]+=dp[i-1][j-k];
for(int i=3*n;i<=4*n;i++)
ans+=dp[n][i];
ll sum = mpow(4,n);
ll g = gcd(sum,ans);
printf("%lld/%lld\n",ans/g,sum/g);
return 0;
}
D.小A的位运算
链接:https://ac.nowcoder.com/acm/contest/549/D
来源:牛客网
题目描述
位运算是一个非常重要的东西。而小A最近在学习位运算,小A看到了一道很简单的例题,是说从N个数里面选出N-1个数要让它们或起来的值最大,小A想知道这个答案是多少。你可以帮帮他吗?
输入描述:
第一行一个整数N表示有N个数接下来一行N个数表示A1,A2...AN第一行一个整数N表示有N个数接下来一行N个数表示A1,A2...AN
输出描述:
一行输出个结果代表最大值一行输出个结果代表最大值
示例1
输入
复制
5
1 2 4 8 16
输出
复制
30
说明
选择2,4,8,16或的和是最大的,没有比这个更大的方案。
备注:
1≤N≤5e6,1≤Ai≤longlong
这题我是排序后贪心,复杂度O(nlogn)
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define ll long long
int main(){
ll a[5000005];
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
sort(a,a+n);
ll sum = a[n-1];
int cnt=1;
for(int i=n-2;i>=0;i--)
if((sum|a[i])>sum){
sum = sum|a[i];
cnt++;
if(cnt>=n-1)
break;
}
printf("%lld\n",sum);
return 0;
}
预处理了一下前缀和后缀,然后枚举那个不选的数就可以了。O(n)
预处理前i个数的或前缀和还有后i个数的或后缀和
通过前缀和与后缀和的或运算,l[i-1]|r[i+1],可以算出除i这个点的所有数的或和
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define Rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef long double ld;
const int inf=0x3f3f3f3f;
const ll INF=9e18;
const int N=1e6+50;
ll a[N],l[N],r[N];
int main() {
int n;
ll ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) l[i]=l[i-1]|a[i];
for(int i=n;i>=1;i--) r[i]=r[i+1]|a[i];
for(int i=1;i<=n;i++) ans=max(ans,l[i-1]|r[i+1]);
printf("%lld\n",ans);
return 0;
}
E.小A的路径
链接:https://ac.nowcoder.com/acm/contest/549/E
来源:牛客网
题目描述
小A来到了一个陌生的城镇,这个城镇与其它城镇之间构成了集群。城镇之间的路径都是单向的,而小A每一天都能由一个城镇走到另外一个城镇。小A将会连续走k天,直到抵达某个城镇。也许他并不能走到这个城镇,那么可以认为不存在这样的路径,也就是路径数为0。否则就会有若干条路径可以抵达某个城镇。现在他想知道,如果他从给定某个城市出发,k天之后到达其它城镇的路径的总和是多少。数据不保证没有重边,也就是说可能每一天从一个城镇到另外一个城镇之间会有多条路径。路径总和可能会非常大,对答案模上1000000007。
输入描述:
第一行三个整数N,M,K,S分别表示一共有N个城镇,城镇之间有M条单向边。K表示小A连续走K天。S表示小A出发的那个城镇。接下来的M行每行两个整数u,v表示从城镇u连了一条有向边到城镇v。第一行三个整数N,M,K,S分别表示一共有N个城镇,城镇之间有M条单向边。K表示小A连续走K天。S表示小A出发的那个城镇。接下来的M行每行两个整数u,v表示从城镇u连了一条有向边到城镇v。
输出描述:
一行输出一个结果,表示小A到其余城镇路径数的总和。一行输出一个结果,表示小A到其余城镇路径数的总和。
示例1
输入
复制
4 5 2 1
1 2
1 3
2 3
4 1
3 4
输出
复制
2
说明
经过2天,小A可以走到3号城镇或者4号城镇,到3号城镇的路径有一条是1-2-3,到4号城镇的路径也是一条是1-3-4,共计有两条路径。
备注:
1≤N≤100, 1≤K≤1e9
这题考的是离散数学的内容qaq,点到点的矩阵的几何意义就是能到该点的路径数
矩阵的乘法运算不可避免的O(n^3),幂运算可以用矩阵快速幂优化O(logk),所有总复杂度为O(n^3logk)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const int N = 150;
int n,m,k,s,x,y;
struct Matrix{
ll a[N][N];
Matrix() { memset(a,0,sizeof(a)); }
Matrix operator * (const Matrix &rhs) const{
Matrix res;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
res.a[i][j] = (res.a[i][j]+a[i][k]*rhs.a[k][j])%mod;
return res;
}
}res,G;
void mpow(int n){
while(n>0){
if(n%2==1)
res=res*G;
G=G*G;
n>>=1;
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&k,&s);
for(int i=1;i<=n;i++)
res.a[i][i] = 1;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
G.a[x][y]++;
}
mpow(k);
ll ans=0;
for(int i=1;i<=n;i++)
if(i!=s)
ans = (ans+res.a[s][i])%mod;
printf("%lld\n",ans);
return 0;
}
F.小A的最短路
链接:https://ac.nowcoder.com/acm/contest/549/F
来源:牛客网
题目描述
小A这次来到一个景区去旅游,景区里面有N个景点,景点之间有N-1条路径。小A从当前的一个景点移动到下一个景点需要消耗一点的体力值。但是景区里面有两个景点比较特殊,它们之间是可以直接坐观光缆车通过,不需要消耗体力值。而小A不想走太多的路,所以他希望你能够告诉它,从当前的位置出发到他想要去的那个地方,他最少要消耗的体力值是多少。
输入描述:
第一行一个整数N代表景区的个数。
接下来N-1行每行两个整数u,v代表从位置u到v之间有一条路径可以互相到达。
接下来的一行两个整数U,V表示这两个城市之间可以直接坐缆车到达。
接下来一行一个整数Q,表示有Q次询问。
接下来的Q行每行两个整数x,y,代表小A的位置在x,而他想要去的地方是y。
输出描述:
对于每个询问下x,y输出一个结果,代表x到y消耗的最少体力对于每个询问下x,y输出一个结果,代表x到y消耗的最少体力
示例1
输入
复制
4
1 2
1 3
2 4
3 4
2
1 3
3 4
输出
复制
1
0
备注:
1≤N≤3e5, 1≤Q≤1e6
图论,感觉这不是常规的最短路呢,dijkstra,floyd好像都不行( •̀ ω •́ )y
没学lca,贴个代码先
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define Rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef long double ld;
const int inf=0x3f3f3f3f;
const ll INF=9e18;
const int N=3e5+50;
struct edge{ int v,w; };
vector<edge> G[N];
int n,U,V,dep[N],Fa[N][22];
void dfs(int u,int fa) {
dep[u]=dep[fa]+1;
Fa[u][0]=fa;
for(int i=1;(1<<i)<=dep[u];i++) Fa[u][i]=Fa[Fa[u][i-1]][i-1];
for(auto &e:G[u]) {
int v=e.v;
if(v==fa) continue;
dfs(v,u);
}
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--) if((1<<i)<=dep[x]-dep[y]) x=Fa[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--) if(Fa[x][i]!=Fa[y][i]) x=Fa[x][i],y=Fa[y][i];
return Fa[x][0];
}
int solve(int x,int y) { return dep[x]+dep[y]-2*dep[lca(x,y)]; }
struct node{
int u,dis;
node() {}
node(int u,int dis):u(u),dis(dis) {}
bool operator <(const node &rhs) const {
return dis>rhs.dis;
}
};
int dis[N];
priority_queue<node> q;
void spfa() {
for(int i=1;i<=n;i++) dis[i]=inf;
dis[U]=0;
q.push({U,0});
while(!q.empty()) {
int u=q.top().u,d=q.top().dis;q.pop();
if(dis[u]<d) continue;
for(auto &e:G[u]) {
int v=e.v,w=e.w;
if(dis[v]>dis[u]+w) {
dis[v]=dis[u]+w;
q.push({v,dis[v]});
}
}
}
}
int main() {
scanf("%d",&n);
for(int i=1;i<n;i++) {
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back({v,1});
G[v].push_back({u,1});
}
dfs(1,0);
scanf("%d%d",&U,&V);
G[U].push_back({V,0});
G[V].push_back({U,0});
spfa();
int q;
scanf("%d",&q);
while(q--) {
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",min(solve(x,y),dis[x]+dis[y]));
}
return 0;
}
G.小A与小B
链接:https://ac.nowcoder.com/acm/contest/549/G
来源:牛客网
题目描述
小A与小B这次两个人都被困在了迷宫里面的两个不同的位置,而他们希望能够迅速找到对方,然后再考虑如何逃离迷宫的事情。小A每次可以移动一个位置,而小B每次可以移动两次位置,小A移动的方向是上下左右左上左下右上右下8个方向,小B移动的方向是上下左右4个方向,请问他们最早什么时候能够找到对方,如果他们最终无法相遇,那么就输出”NO"。
输入描述:
第一行两个整数N,M分别表示迷宫的行和列。接下来一个N×M的矩阵其中"C"表示小A的位置,"D"表示小B的的位置,"#"表示不可通过的障碍,"."则是可以正常通过的位置。字符用空格隔开第一行两个整数N,M分别表示迷宫的行和列。接下来一个N×M的矩阵其中"C"表示小A的位置,"D"表示小B的的位置,"#"表示不可通过的障碍,"."则是可以正常通过的位置。字符用空格隔开
输出描述:
如果可以相遇,第一行输出一个YES,第二行一个整数输出最短的相遇时间。
否则就输出一个NO表示不能相遇。
示例1
输入
复制
4 5
. . . . .
. # # # .
. . . # D
. . C # .
输出
复制
YES
3
备注:
1≤n,m≤1000
双向bfs,这题通过率最低,果然大家都是小萌新hah
这题使我对bfs及搜索有了进一步的理解= =
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1005;
int n,m,g1,g2,g3,g4,id;
struct Node{
int x,y;
};
char ch[N][N];
bool vis[2][N][N];//0代表A,1代表B
int dx[8] = {1,0,-1,0,1,1,-1,-1};
int dy[8] = {0,1,0,-1,-1,1,1,-1};
queue<Node> q[2];
int bfs(int id){//不循环只做一步的bfs
int sz = q[id].size();
while(sz--){//!q[id].empty())会一步执行到维,这里我们只希望它执行一层
Node u = q[id].front();
q[id].pop();
for(int i=0;i<4+(id==0?4:0);i++){
int x = u.x + dx[i];
int y = u.y + dy[i];
if(x>=1 && x<=n && y>=1 && y<=m && ch[x][y]!='#' && !vis[id][x][y] ){
if(vis[id^1][x][y])
return 1;//该点被另一个人访问过了,说明他们在这层可以相遇了
vis[id][x][y] = true;
q[id].push({x,y});
}
}
}
return 0;
}
int solve(){
q[0].push({g1,g2});
q[1].push({g3,g4});
vis[0][g1][g2] = vis[1][g3][g4] = true;
int step=0;
while(!q[0].empty() || !q[1].empty()){
step++;
if(bfs(0))
return step;
if(bfs(1))
return step;
if(bfs(1))
return step;
}
return -1;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>ch[i][j];
if(ch[i][j]=='C')
g1=i,g2=j;
else if(ch[i][j]=='D')
g3=i,g4=j;
}
int ans =solve();
if(ans==-1)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl<<ans<<endl;
return 0;
}