华东交通大学2018年ACM“双基”程序设计竞赛部分题解

链接:https://ac.nowcoder.com/acm/contest/221/C
来源:牛客网

C-公式题(2)
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

令f(n)=2*f(n-1)+3*f(n-2)+n,f(1)=1,f(2)=2
令g(n)=g(n-1)+f(n)+n*n,g(1)=2
告诉你n,输出g(n)的结果,结果对1e9+7取模

输入描述:

多组输入,每行一个整数n(1<=n<=1e9),如果输入为0,停止程序。

输出描述:

在一行中输出对应g(n)的值,结果对1e9+7取模。

输入例子:
1
5
9
456
0
输出例子:
2
193
11956
634021561

-->

示例1

输入

复制

1
5
9
456
0

输出

复制

2
193
11956
634021561

说明

多组输入,输入为0时,终止程序

备注:

项数极大,朴素算法无法在规定时间内得出结果

解题思路:很明显的矩阵快速幂,比赛时没推出来。推出来了就是一个矩阵快速幂的板子题。
推倒方法可以参照:https://www.jianshu.com/p/25eba927d9da
推倒过程:
g(n)=g(n-1)+f(n)+n*n;
g(n)=g(n-1)+2*f(n-1)+3*f(n-2)+n*n+n;
g(n)=g(n-1)+2*f(n-1)+3*f(n-2)+(n-1)^2+3*(n-1)+2;

华东交通大学2018年ACM“双基”程序设计竞赛部分题解

得到矩阵:

华东交通大学2018年ACM“双基”程序设计竞赛部分题解

再通过简单的递推一下,左边n代3,可以得到一个常数矩阵,再乘以右边矩阵的n-2次方,得到矩阵的第一行第一列元素即为答案。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int mod=1e9+;
const int maxn=;
typedef long long ll;
ll n;
struct Matrix{
ll a[maxn][maxn];
}; Matrix mul(Matrix a,Matrix b) //两矩阵相乘
{
Matrix temp;
memset(temp.a,,sizeof(temp.a));
for(int i=;i<maxn;i++)
for(int j=;j<maxn;j++)
for(int k=;k<maxn;k++)
temp.a[i][j]=(temp.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
return temp;
} Matrix qpow(Matrix a,ll n) //矩阵快速幂
{
Matrix ans;
memset(ans.a,,sizeof(ans.a));
for(int i=;i<maxn;i++)
ans.a[i][i]=; //化成单位矩阵
while(n)
{
if(n&) ans=mul(ans,a);
a=mul(a,a);
n/=;
}
return ans;
} int main()
{
Matrix A,C;
A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=;
A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=;
A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=;
A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=;
A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=;
A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=,A.a[][]=; C.a[][]=,C.a[][]=,C.a[][]=,C.a[][]=,C.a[][]=,C.a[][]=;
while(cin>>n)
{
if(n==) break;
if(n==)
{
cout<<<<endl;
continue;
}
cout<<mul(C,qpow(A,n-)).a[][]<<endl;
}
return ;
}

链接:https://ac.nowcoder.com/acm/contest/221/G
来源:牛客网

G-7的意志
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

定义一个序列a:7,77,777......,7777777(数字全为7的正整数,且长度可以无限大)
clearlove7需要从含有7的意志的数里获得力量,如果一个整数能被序列a中的任意一个数字整除,并且其数位之和为序列a中任意一个数字的倍数,那么这个数字就含有7的意志,现在给你一个范围[n,m],问这个范围里有多少个数字含有7的意志。

输入描述:

多组输入,每行两个个整数n,m(1<=n<=m<=1e18),如果输入为"0 0",停止程序。

输出描述:

每一行输出含有7的意志的数的个数。

输入例子:
1 7
1 100
1 1000
0 0
输出例子:
1
3
21

-->

示例1

输入

复制

1 7
1 100
1 1000
0 0

输出

复制

1
3
21

说明

1到100中符合条件的数字为7,70,77
数位dp,不会可以参考博客:https://blog.csdn.net/brazy/article/details/77427699
定义一个三维dp,分别存储到枚举的当前位置,模7的余数,数位和模7的余数。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-;
const int INF = 0x3f3f3f3f;
const ll mod=1e9+;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll n,m,a[];
ll dp[][][];
ll dfs(ll pos,ll pre,ll sum,bool limit)
{
if(pos==-)
return (pre%==&&sum%==); //枚举到了最后一位并且合法
if(!limit&&dp[pos][pre][sum]!=-)
return dp[pos][pre][sum];
ll up=limit?a[pos]:;
ll ans=;
for(int i=;i<=up;i++)
ans+=dfs(pos-,(pre*+i)%,(sum+i)%,limit&&i==a[pos]);
if(!limit) //记忆化
dp[pos][pre][sum]=ans;
return ans;
}
ll solve(ll x)
{
ll pos=;
while(x) //对数进行数位拆分
{
a[pos++]=x%;
x/=;
}
return dfs(pos-,,,true); //从最高位开始枚举
}
int main()
{
ios_base::sync_with_stdio(false); cin.tie();
memset(dp, -, sizeof(dp));
while(cin>>n>>m)
{
if(n==&&m==) break;
cout<<solve(m)-solve(n-)<<endl;
}
return ;
}

链接:https://ac.nowcoder.com/acm/contest/221/I
来源:牛客网

I-数学题
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

题面描述
最近,华东交通大学ACM训练基地的老阿姨被一个数学问题困扰了很久,她希望你能够帮她解决这个问题。
这个数学问题是这样的,给你一个N,要求你计算
华东交通大学2018年ACM“双基”程序设计竞赛部分题解
gcd(a,b)表示a和b的最大公约数

输入描述:

多组输入,每行一个整数n(1<=n<=10^14)。

输出描述:

每行一个整数,表示答案。由于答案会很大你要对1000000007取模。

输入例子:
4
10
输出例子:
6
35

-->

示例1

输入

复制

4
10

输出

复制

6
35

说明

样例一,2+4=6。
样例二,2+4+5+6+8+10=35。 思路:欧拉公式的延伸:对于一个数,与其互质的数的总和是euler(n)*n/2。
所以结果就是n*(n+1)/2-euler(n)*n/2. 注意取模不能直接除2,需要乘2对1e9+7的逆元。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+;
ll n,m; ll euler(ll n)
{
ll ans=n;
ll i;
for(i=;i<=sqrt(n);i++)
{
if(n%i==)
{
ans=ans/i*(i-);
while(n%i==)
n/=i;
}
}
if(n>)
ans=ans/n*(n-);
return ans;
}
ll pow_mod(ll a, ll b)
{
ll ret = ;
ll p=mod;
while(b)
{
if(b & )
ret = (ret * a) % p;
a = (a * a) % p;
b >>= ;
}
return ret;
}
int main()
{
while(cin>>n)
{
if(n==)
{
cout<<<<endl;
continue;
}
ll inv=pow_mod(,mod-);
cout<<((n%mod)*((n+-euler(n))%mod)%mod)*inv%mod<<endl;
}
return ;
}

链接:https://ac.nowcoder.com/acm/contest/221/K
来源:牛客网

MIKU酱的氪金宝典
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

MIKU酱是个玩游戏氪金的人,游戏公司给她制定了新的规则,如果想从关卡i到关卡j,你需要交一些钱就可以了,但同时,MIKU酱的爸爸zjw很爱她,所以她可以每过一关就向她爸要一次钱,但她爸每次给他的钱是固定的,MIKU酱是个不会节省的女孩,哪怕每次多出来的钱,她也会拿去买肥宅快乐水,所以每次要的钱一定花完,因为MIKU酱不想挨骂,所以希望每次他爸给她的钱最少。
tips(到达第n关即通过,每到达一关一定能通过这关)

输入描述:

多组输入,每个样例第一行输入两个整数n,m(2<=n<=200,1<=m<=1000)表示关卡和规则的数量,接下来m行规则,每行输入x,y,w(w<=1000),表示从关卡x到y需要缴纳w的费用,保证题目有解,不会出现x=y的情况

输出描述:

输出一行,代表最少的钱

输入例子:
4 4
1 2 2
1 3 1
2 4 3
3 4 1
输出例子:
1

-->

示例1

输入

复制

4 4
1 2 2
1 3 1
2 4 3
3 4 1

输出

复制

1
思路:n的范围不大,三层循环就可以过,但是比赛时没想到,比赛用的dfs,但是超时,经过剪枝了下,还是过了,题解用的dijkstra
dfs是记录两个东西,一个当前位置,一个该条路的边权最大值,走到节点n时,ans保留dfs出的最小值。
dijkstra做法直接把松弛操作改改就可以了 dfs做法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+;
const ll inf=0x3f3f3f3f;
ll n,m;
typedef pair<int,int> P;
vector<P> mp[];
int vis[],ans;
void dfs(int x,int cost) //x为当前位置,cost为该条路的边权最大值
{
if(x==n)
{
ans=min(ans,cost);
return;
}
if(cost>=ans) //剪枝,如果还未到n就已经比现在最优结果大直接返回,去掉会超时
return;
int ss=mp[x].size();
for(int i=;i<ss;i++)
{
if(vis[mp[x][i].first]==)
{
vis[mp[x][i].first]=;
dfs(mp[x][i].first,max(cost,mp[x][i].second));
vis[mp[x][i].first]=;
}
}
}
int main()
{
while(cin>>n>>m)
{
ios_base::sync_with_stdio(false); cin.tie();
for(int i=;i<=n;i++) mp[i].clear();
ans=inf;
memset(vis,,sizeof(vis));
for(int i=;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
P q;
q.first=v;
q.second=w;
mp[u].push_back(q);
}
vis[]=;
dfs(,);
cout<<ans<<endl;
}
return ;
}

dijkstra做法:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-;
const int INF = 0x3f3f3f3f;
const ll mod=1e9+;
int n,m,dis[],vis[];
struct qnode{
int v,d;
qnode(int a,int b):v(a),d(b){}
bool operator<(const qnode& a)const{
return d>a.d;
}
};
struct edge{
int v,w;
edge(int a,int b):v(a),w(b){}
};
vector<edge> mp[];
priority_queue<qnode> pq;
void dij()
{
memset(vis,,sizeof(vis));
memset(dis,INF,sizeof(dis));
dis[]=;
pq.push(qnode(,));
while(!pq.empty())
{
qnode x=pq.top();
pq.pop();
int u=x.v;
for(int i=;i<mp[u].size();i++)
{
int v=mp[u][i].v;
int w=mp[u][i].w;
int tmp=dis[u];
tmp=max(dis[u],w);
if(dis[v]>tmp) //如果该条路边权最大值小于当前最优值,则更新
{
dis[v]=tmp;
pq.push(qnode(v,dis[v]));
}
}
}
} int main()
{
ios_base::sync_with_stdio(false); cin.tie();
while(cin>>n>>m)
{
for(int i=;i<=n;i++) mp[i].clear();
for(int i=;i<m;i++)
{
int u,v,w;
cin>>u>>v>>w;
mp[u].push_back(edge(v,w));
}
dij();
cout<<dis[n]<<endl;
}
return ;
}
上一篇:【题解】求细胞数量-C++


下一篇:Redirect and POST in ASP.NET