第一届佳木斯大学程序设计校赛题解

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.线路(最短路)

思路

没啥好说的,裸的最短路,可以用FloydDijkstraSpfa等等做法,因为数据范围很小

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;
以上是关于第一届佳木斯大学程序设计校赛题解的主要内容,如果未能解决你的问题,请参考以下文章

河南工业大学2017校赛题解

南昌大学航天杯第二届程序设计竞赛校赛网络同步赛题解

第一届河北工业大学程序设计竞赛校赛 个别题的解析

2018年东北农业大学春季校赛----不完整题解

新年第一篇!西南民族大学第十届校赛(同步赛)

南昌大学航天杯第二届程序设计竞赛校赛网络同步赛 G