第一届佳木斯大学程序设计校赛题解
Posted MangataTS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一届佳木斯大学程序设计校赛题解相关的知识,希望对你有一定的参考价值。
前言
这场比赛又很多原题,总体的难度不高,但是也不是说没有难度,C题和K还是能让我学习到一些东西,B题帮我复习了一下Trie
树,总之这场AK了,真不容易,由于本人比较菜如果有问题欢迎在评论区指出。
A.斐波那契数列(循环)
思路
因为数据范围很小所以我们直接使用循环去跑即可,如果数据范围很大(eg: n > 1 e 8 n>1e8 n>1e8)那么我们可以用矩阵快速幂求解,学习链接:https://www.bilibili.com/video/BV1j3411e7qh/
循环代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 1000000009
#define endl "\\n"
#define PII pair<int,int>
ll ksm(ll a,ll b)
ll ans = 1;
for(;b;b>>=1LL)
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
return ans;
ll lowbit(ll x)return -x & x;
const int N = 2e6+10;
ll f[N];
int main()
ll n;
cin>>n;
f[1]=f[2] = 1;
for(ll i = 3;i <= n; ++i)
f[i] = f[i-1]+f[i-2];
cout<<f[n]<<endl;
return 0;
矩阵快速幂代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 100000000000000007
#define N 200
struct Matrix
ll mp[N][N];
;
ll n,k;
Matrix operator*(const Matrix &x, const Matrix &y)
Matrix a;
memset(&a,0,sizeof(a));
for(int i = 1;i <= n; ++i)
for(int j = 1;j <= n; ++j)
for(int k = 1;k <= n; ++k)
a.mp[i][j] = ((a.mp[i][j] + x.mp[i][k] * y.mp[k][j]) % mod) %mod;
return a;
int main()
Matrix ans,loc;
n = 2;
scanf("%lld",&k);
for(int i = 1;i <= n; ++i)
ans.mp[i][i] = 1;
loc.mp[1][1] = 0;
loc.mp[1][2] = loc.mp[2][1] = loc.mp[2][2] = 1;//初始化
while(k)
if(k & 1) ans = ans * loc;
loc = loc * loc;
k>>=1;
ll res = ans.mp[2][1] % mod;
printf("%lld\\n",res);
return 0;
B.统计(Trie树)
思路
我们通过trie
树将N个S串插入进去,然后通过exit
数组存储一下这个字符串的数量,然后通过子trie
树的find计算出有多少匹配的前缀字符串
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 5e5+10;//请注意这里的数组大小要开字符串的个数*字符串的长度这么大
int n,m;
struct trie
int nextt[N][26],cnt;//nextt[i][j]存储的是第i个字符下一个字符j的位置信息
int exit[N];//exit[i]表示以值为i结束的这个字符串是否存在
void insert(char *s,int l)
int p = 0;//这个表示的是当前的位置指针
for(int i = 0;i < l; ++i)
int c = s[i] - 'a';
if(!nextt[p][c]) nextt[p][c] = ++cnt;//如果下面一层是空的,那么就更新下一层的值
p = nextt[p][c];//更新当前的p指针
exit[p]++;
int find(char *s,int l)
int ans = 0,p=0;
for(int i = 0;i < l; ++i)
int ch=s[i]-'a';
p=nextt[p][ch];
if(!p) return ans;
ans+=exit[p];
return ans;
T;
char S[N];
int main()
scanf("%d%d",&n,&m);
for(int i = 0;i < n; ++i)
scanf(" %s",S);
T.insert(S,strlen(S));
scanf("%d",&m);
for(int i = 0;i < m; ++i)
scanf("%s",S);
printf("%d\\n",T.find(S,strlen(S)));
return 0;
C.沙拉公主的困惑(欧拉函数)
思路
思路参考这两位大佬的博客:
https://www.cnblogs.com/butterflydew/p/9713540.html
https://www.cnblogs.com/yangyaojia/p/6434611.html
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e7;
const int M=7e5;
bool ispri[N+10];
int pri[M],cnt,fr[M],num[M],fac[N+10],numfac[N+10],r,t,n,m;
int mul(int a,int b)
ll c=(ll)(a)*b%r;
return (int)(c);
int inv(int b,int k)
int f=1;
while(k)
if(k&1) f=mul(f,b);
b=mul(b,b);
k>>=1;
return f;
void init()
memset(ispri,true,sizeof(ispri));
for(int i=2;i<=N;i++)
if(ispri[i])
pri[++cnt]=i;
for(int j=1;j<=cnt&&pri[j]*i<=N;j++)
ispri[pri[j]*i]=false;
if(i%pri[j]==0) break;
fr[0]=1;
for(int i=1;i<=cnt;i++)
num[i]=num[i-1];
fr[i]=fr[i-1];
if(pri[i]-1==r)
++num[i],fr[i]=mul(fr[i],inv(pri[i],r-2));
else if(pri[i]==r)
--num[i],fr[i]=mul(fr[i],pri[i]-1);
else
fr[i]=mul(fr[i],mul(inv(pri[i],r-2),pri[i]-1));
fac[0]=1;
for(int i=1;i<=N;i++)
fac[i]=fac[i-1];
numfac[i]=numfac[i-1];
int d=i;
while(d%r==0) ++numfac[i],d/=r;
fac[i]=mul(fac[i],d);
int main()
scanf("%d%d",&t,&r);
init();
while(t--)
scanf("%d%d",&n,&m);
int pos=std::upper_bound(pri+1,pri+1+cnt,m)-pri-1;
int ans=mul(fac[n],fr[pos]);
if(num[pos]+numfac[n]==0) printf("%d\\n",ans);
else printf("0\\n");
return 0;
D.和(大数运算)
思路
这个要么写一个C++的高精度乘法和加法去做,或者使用JAVA的BigInterger
,或者直接用python
做,我这里为了方便直接使用的是python
代码
n=int(input(""))
ans=0
cnt=1
for i in range(1,n+1):
cnt*=i
ans+=cnt
print(ans)
E.线路(最短路)
思路
没啥好说的,裸的最短路,可以用Floyd
、Dijkstra
、Spfa
等等做法,因为数据范围很小
Dijkstra代码
#include<cstdio>
#include<cstring>
#include<queue>//
using namespace std;
const int N=2e5+5;//数据范围
struct edge//存储边
int u,v,w,next;//u为起点,v为终点,w为权值,next为前继
;
edge e[N];
int head[N],dis[N],n,m,s,cnt;//head为链中最上面的,dis表示当前答案,n为点数,m为边数,s为起点,cnt记录当前边的数量
bool vis[N];//vis表示这个点有没有走过
struct node
int w,to;//w表示累加的权值,to表示到的地方
bool operator <(const node &x)const//重载“<”号
return w>x.w;
;
priority_queue<node>q;//优先队列(堆优化)
void add(int u,int v,int w)
++cnt;//增加边的数量
e[cnt].u=u;//存起点
e[cnt].v=v;//存终点
e[cnt].w=w;//存权值
e[cnt].next=head[u];//存前继
head[u]=cnt;//更新链最上面的序号
//链式前向星(加边)
void Dijkstra()
memset(dis,0x3f,sizeof(dis));//初始化,为dis数组附一个极大值,方便后面的计算
dis[s]=0;//起点到自己距离为0
q.push(node0,s);//压入队列
while(!q.empty())//队列不为空
node x=q.top();//取出队列第一个元素
q.pop();//弹出
int u=x.to;//求出起点
if(vis[u]) continue;//已去过就不去了
vis[u]=true;//标记已去过
for(int i=head[u];i;i=e[i].next)
int v=e[i].v;//枚举终点
if(dis[v]>dis[u]+e[i].w)//若中转后更优,就转
dis[v]=dis[u]+e[i].w;//更新
q.push(nodedis[v],v);//压入队列
int main()
int u,v,w = 1;
s = 1;
scanf("%d%d%d%d",&n,&m,&s,&n);//输入
for(int i=1;i<=m;++i)
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
Dijkstra();//DJ
if(dis[n]==0x3f3f3f3f)
puts("-1");
else
printf("%d\\n",dis[n]);//输出1-n的最短路
return 0;
以上是关于第一届佳木斯大学程序设计校赛题解的主要内容,如果未能解决你的问题,请参考以下文章