G题 (数位dp)
描述:
链接:https://ac.nowcoder.com/acm/contest/221/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
输入
复制
1 7
1 100
1 1000
0 0
输出
复制
1
3
21
说明
1到100中符合条件的数字为7,70,77
参考链接:HDU2089 不要62【数位DP+记忆化搜索】
代码:
/* ecjtu 双基 */
/* 7的意志 */
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
#define ll long long
const int mod=1e9+7;
int digits[20];
ll dp[20][10][10];
/*变量
dp[pos][pre][sum] - pos位到0位 的数的和%7的余数为pre,数位和%7为sum的 个数
sum - 数位和的余数
pre - 数的余数 例如113,pre最先取1 -> pre=(pre*10+1)%7 -> pre=(pre*10+3)%7
//之前刚刚学了数位dp,比赛时结果这道题还是做不来,看来没有理解透数位dp,不过这道题可以让我更加熟悉
*/
ll dfs(int pos,int pre, int sum,bool limit)
{
if(pos==-1) return (sum%7==0&&pre%7==0);
if(!limit&&dp[pos][pre][sum]!=-1) return dp[pos][pre][sum];
int up=limit?digits[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++)
{
ans=ans+dfs(pos-1,(pre*10+i)%7,(sum+i)%7,limit&&i==digits[pos]);
}
if(!limit&&dp[pos][pre][sum]==-1)
dp[pos][pre][sum]=ans;
return ans;
}
ll solve(ll n)
{
int len=0;
memset(dp,-1,sizeof(dp));
while(n)
{
digits[len++]=n%10;
n/=10;
}
return dfs(len-1,0,0,true);
}
int main()
{
ll n, m;
while(scanf("%lld%lld",&n, &m)!=EOF)
{
if(n==0&&m==0) break;
printf("%lld\n",solve(m)-solve(n-1));
}
return 0;
}
I题:
描述
链接:https://ac.nowcoder.com/acm/contest/221/I
来源:牛客网
数学题
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
题面描述
最近,华东交通大学ACM训练基地的老阿姨被一个数学问题困扰了很久,她希望你能够帮她解决这个问题。
这个数学问题是这样的,给你一个N,要求你计算
gcd(a,b)表示a和b的最大公约数
输入描述:
多组输入,每行一个整数n(1<=n<=10^14)。
输出描述:
每行一个整数,表示答案。由于答案会很大你要对1000000007取模。
示例1
输入
复制
4
10
输出
复制
6
35
说明
样例一,2+4=6。
样例二,2+4+5+6+8+10=35。
题意:
求1到n之间与n不互质的数的总和
问题分析:
这题可以换成用总和(即n*(n+1)/2)减去1到n之间与n互质的数的总和
1.求1到n之间与n互质的数的总和 ,就是n*phi(n)/2, phi(n)就是1到n之间与n互质的
个数,证明见下面的参考链接[1到n之间与n互质的数的总和]
2.由于最后结果是要取余的,故要将除以2转换成乘以2模mod(这题mod是e9+7),
然后在取余。有关逆元的用处见下面参考链接[逆元]
- 参考链接:
欧拉函数
1到n之间与n互质的数的总和
逆元
c++编程语言如下:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const LL mod = 1000000007 ;
LL phi(LL x){
LL ans = x;
for(LL i = 2; i*i <= x; i++){
if(x % i == 0){
ans = ans / i * (i-1);
while(x % i == 0) x /= i;
}
}
if(x > 1) ans = ans / x * (x-1);
return ans;
}
LL quick(LL x, LL n){
LL res = 1LL ;
while( n ){
if(n&1)res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
int main(){
LL n ;
while( ~scanf("%lld",&n) ){
//N*phi(N)/2
LL res ;
if( n == 1 ){
cout << 0 << endl;
continue ;
}
LL ans = (((n%mod)*((n+1)%mod))%mod)*quick(2,mod-2)%mod-(((n%mod)*(phi(n)%mod))%mod)*quick(2,mod-2)%mod;
ans %= mod;
printf("%lld\n",(ans+mod)%mod);
}
return 0 ;
}
K题:
描述:
链接: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的情况
输出描述:
输出一行,代表最少的钱
示例1
输入
复制
4 4
1 2 2
1 3 1
2 4 3
3 4 1
输出
复制
1
代码:
/* ecjtu 双基 */
/* 7的意志 */
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
ll e[225][225];
ll n,m;
void flyod()
{
for(ll k=1;k<=n;k++)
for(ll i=1;i<=n;i++)
for(ll j=1;j<=n;j++)
{
ll t=max(e[i][k],e[k][j]);
if(e[i][j]>t)
e[i][j]=t;
}
cout<<e[1][n]<<endl;
}
int main()
{
int x,y, z;
while(scanf("%lld%lld",&n,&m)!=EOF)
{
memset(e,INF,sizeof(e));
while(m--)
{
scanf("%d%d%d",&x,&y,&z);
if(z<e[x][y]) e[x][y]=z;
}
for(ll i=1;i<=n;i++)
e[i][i]=0;
flyod();
}
return 0;
}