难度预估
Easy:JFBH
Middle:AEKC
Hard:GDIL
实际效果
Easy:JFB
Middle:HC
Hard:KAEGD
???:LI
感想
思维题杀疯局
A题卡住的人有点多
题解
题解按原先设计的难度排序,
E和L两个题有出题人题解,
剩下的题解都是我(G题出题人)写的
J.搴檤(签到)
签到题,按题意累加求出第k项
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll k,f[1005];
int main(){
scanf("%lld%lld%lld",&f[0],&f[1],&k);
//if(k>40)while(1);
assert(0<=f[0] && f[0]<=10);
assert(0<=f[1] && f[1]<=10);
assert(0<=k && k<=60);
for(int i=2;i<=k;++i){
f[i]=f[i-1]+f[i-2];
}
printf("%lld\n",f[k]);
return 0;
}
F.小漳爱探险!(模拟)
最长回文串问题:
1. 复杂度O(n)的Manacher - OI Wiki
2. 复杂度O(n^2)的区间dp,dp[l][r]表示区间[l,r]是否是一个回文串
3. 复杂度O(n^2)的枚举回文中心,然后向两侧扩展,暴力判断
由于字符串是完全随机的,可以实际假设存在长为x的回文串,算一下出现长度为x的回文串概率,
发现实际出现很长的回文串的概率很小,实际随机的数据中,只有长度不超过15的回文串
因此,可以暴力从回文中心向两侧扩展,复杂度O(15*n)
需要注意需要循环两次,分别对应回文串长度为奇数/偶数的情形,
没看见写dp[i][20]的同学,倒是看见有抄manacher板子的同学,我的评价是寄
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
typedef pair<int,int> P;
int n,ans;
char s[N];
int main(){
scanf("%d",&n);
assert(1<=n && n<=1000000);
scanf("%s",s+1);
assert(strlen(s+1)==n);
for(int i=1;i<=n;++i){
assert(!(s[i]<'a' || s[i]>'z'));
}
for(int i=1;i<=n;++i){
int l=i,r=i;
while(l>=1 && r<=n && s[l]==s[r])l--,r++;
ans=max(ans,r-l-1);
}
for(int i=2;i<=n;++i){
int l=i-1,r=i;
while(l>=1 && r<=n && s[l]==s[r])l--,r++;
ans=max(ans,r-l-1);
}
printf("%d\n",ans);
return 0;
}
B. lgl学图论(图论/最短路)
最短路 - OI Wiki板子题,用dijkstra跑1号点出发的单源最短路即可,
数据范围1e6,需要堆优化,注意图可能不连通
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int N=1e6+10,INF=0x3f3f3f3f;
int n,m,u,v,w,dis[N];
bool vis[N];
vector<P>e[N];
struct Q{
int d,u;
Q(){}
Q(int dd,int uu):d(dd),u(uu){}
};
bool operator<(Q a,Q b){
return a.d>b.d;
}
priority_queue<Q>q;
void dijkstra(int s){
memset(dis,INF,sizeof dis);
q.push(Q(0,s));
dis[s]=0;
while(!q.empty()){
Q z=q.top();q.pop();
int d=z.d,u=z.u;
if(vis[u])continue;
vis[u]=1;
for(auto &x:e[u]){
int v=x.first,w=x.second;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push(Q(dis[v],v));
}
}
}
}
signed main(){
scanf("%d%d",&n,&m);
assert(1<=n && n<=1000000);
assert(1<=m && m<=1000000);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&u,&v,&w);
assert(1<=u && u<=n);
assert(1<=v && v<=n);
assert(1<=w && w<=1000);
if(u==v)continue;
e[u].push_back(P(v,w));
e[v].push_back(P(u,w));
}
dijkstra(1);
for(int i=1;i<=n;++i){
printf("%d%c",dis[i]==INF?-1:dis[i]," \n"[i==n]);
}
return 0;
}
H.完全二叉树(树/先序遍历、中序遍历、完全二叉树判定)
题解做法及选手做法,基本是还原这棵二叉树+完全二叉树判定,
先递归建树,假设[l1,r1]是当前需要还原的先序区间,与之对应的中序区间为[l2,r2]
根据先序定义,l1一定是当前这棵树的根,在对应的中序区间内找到l1对应的位置,记为pos,
根据中序定义,中序区间[l2,pos)对应根的左子树,与其对应的先序区间为[l1+1,l1+pos-l2];
(pos,r2]对应根的右子树,与其对应的先序区间为[r1-(r2-pos)+1,r1],递归下去即可
完全二叉树判定:
1. 不存在一个点,有右子树,但没有左子树
2. 若按层(同层内先左后右)遍历这棵树,则:
i.若出现了一个点,只有左子树,没有右子树,则这个点之后的所有点,都是叶子节点
ii.若出现了一个叶子节点,则这个点之后的所有点,都是叶子节点
以上只要一条不满足,就不是完全二叉树
也可以采用标号的方式来判断:
设树当前点标号为i,则左儿子标号2*i,右儿子标号2*i+1,
设树节点为n,若不存在任一个点的标号大于n,即为完全二叉树
#include<bits/stdc++.h>
using namespace std;
const int N=30,M=26;
char pre[N],mid[N];
int n,m,cnt1[M],cnt2[M];
int rt,l[M],r[M];
bool lf[M];
int dfs(int l1,int r1,int l2,int r2){
if(l1==r1){
assert(l2==r2);
assert(pre[l1]==mid[l2]);
lf[pre[l1]-'A']=1;
return mid[l2]-'A';
}
int pos=-1;
for(int i=l2;i<=r2;++i){
if(mid[i]==pre[l1]){
pos=i;
break;
}
}
int ls=pos-l2,rs=r2-pos,v=pre[l1]-'A';
if(ls)l[v]=dfs(l1+1,l1+ls,l2,pos-1);
if(rs)r[v]=dfs(l1+ls+1,l1+ls+rs,pos+1,r2);
return v;
}
bool bfs(){
queue<int>q;
q.push(rt);
bool las=0;
while(!q.empty()){
int x=q.front();q.pop();
if(las && !lf[x])return 0;
if(r[x]!=-1 && l[x]==-1)return 0;
if(l[x]!=-1 && r[x]==-1)las=1;
if(l[x]!=-1)q.push(l[x]);
if(r[x]!=-1)q.push(r[x]);
}
return 1;
}
int main(){
memset(l,-1,sizeof l);
memset(r,-1,sizeof r);
scanf("%s%s",pre,mid);
n=strlen(pre);m=strlen(mid);
assert(n==m);assert(n<=26);
for(int i=0;i<n;++i){
assert('A'<=pre[i] && pre[i]<='Z');
assert('A'<=mid[i] && mid[i]<='Z');
cnt1[pre[i]-'A']++;
cnt2[mid[i]-'A']++;
}
for(int i=0;i<26;++i){
assert(cnt1[i]==cnt2[i]);
}
rt=dfs(0,n-1,0,n-1);
puts(bfs()?"YE5":"N0");
return 0;
}
当然也看到了选手的一个逆向做法,就是如果这棵树已经是完全二叉树,
先序访问[1,n]这n个点得到一个先序序列,中序访问[1,n]这n个点得到一个中序序列,
只需检查先序序列和中序序列相同点号的位置,在给定的输入中的字母是否完全一致即可
#include <bits/stdc++.h>
using namespace std;
string s1,s2;
int len;
int cnt;
int a[100];
int first[100],mid[100];
int m1[100];//f字母->数字
int m2[100];//m字母->数字
int ls(int num){return num<<1;}
int rs(int num){return (num<<1)|1;}
void fDFS(int now){

本文详细解析了算法竞赛中的几类经典问题,包括多源最短路、博弈论中的SG函数应用以及概率动态规划。通过实例解析了如何运用并查集、公平组合游戏理论和期望计算来解决这些问题,帮助参赛者提升算法理解与实战能力。
最低0.47元/天 解锁文章
3589

被折叠的 条评论
为什么被折叠?



