19广东工业大学 牛客J H补题

19广东工业大学 牛客补题


害!很久之前刷了一些题目,但是由于课程实在是没时间好好品题(其实是太懒了)决心好好嚼一些当时没有AC的题目!
【注:我是AC一题然后写一题报告的,可能算法有错误或者算法不是最优,本人小白也请见谅 XD】

J-简单的数学

题目链接:J题链接.
题意:
在这里插入图片描述
数据范围:T组样例( T <= 100) x范围(1 <= x <= 109)
思路:
1.想法:导数根据定义是该点的切线的斜率,取个数k=1.00000001 然后带入算斜率巴拉巴拉……
结果:时间TLE,精度WA,代码MAX
2.想法:既然是数学题,那么我们就用数学工具计算。
注意我们只用求f`(1),然后分子有一项是(x-1),很大概率可以化简导数的公式。
多项乘积导数:(abc)’ = a’bc+ab’c+abc’
分式导数:y’=(a’b-ab’)/b²
带入得到:f’(x)=在这里插入图片描述
那么分子后半部分减法后面如果x=1带入都是0了,化简下并带入x=1,得
f’(1)=在这里插入图片描述
看起来可以分子分母约分 化简得到
1/f’(1) = n*(1+n)*(-1)n+1
AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int cas;
    cin >> cas;
    while(cas--){
        ll n;
        cin >> n;
        ll ans = n*(n+1);
        if(n%2==0)ans=-ans;
        cout << ans << endl;
    }
	return 0;
}

H-绵羊的银币

题目链接:H题链接.
题意:
f[0] = 0
f[1] = 1
f[n] = f[n/2] + f[n/4] (n>=2)
快速求f[n]
数据范围:多快?呃呃,查询250000次,n (0<= n <=1018)
思路:1018那一遍遍递归或者循环下来我都要毕业了。。依旧数学工具!
先枚举下f(n)=什么?
在这里插入图片描述
首先看一下分母:第k次推为 f(n/2k) 和 f(n/2k+1)
然后看一下k与系数之间的关系,列个表格得到:

系数1系数2
11
21
32
53
85

可以看到是一个类似递推的系数关系,我们先枚举k为前100项的系数1和系数2

x1[1]=1;x2[1]=1;
for(int i=2;i<=1000;++i){
    x1[i]=x1[i-1]+x2[i-1];
    x2[i]=x1[i-1];
}

我们的意图是f(n)=系数1* f(n/2k) + 系数2* f(n/2k+1)
我们需要把n/2k压得尽可能小,这样f(n/2k) 与 f(n/2k+1) 我们可以轻易枚举
因为k增大1,2k增大一倍,那一个loop就可以搞定求出适当的k了
AC代码:

#include<bits/stdc++.h>
#define show(x) std::cerr << #x << "=" << x << std::endl
using namespace std;
typedef long long ll;
const int MAX = 1e5+50;
const double EPS = 1e-10;
ll cnt[120];				//存f(n)
ll x1[1000],x2[1000];		//存系数1与系数2序列
ll qpow(ll a,ll n){			//为了算2^n 快些
    ll ans=1;
    while(n){
        if(n&1){
            ans=ans*a;
        }
        n>>=1;
        a=a*a;
    }
    return ans;
}
int main()
{
    int cas;
    scanf("%d",&cas);
    cnt[0]=0;cnt[1]=1;
    for(int i=2;i<=100;++i){
        cnt[i]=cnt[i/2]+cnt[i/4];		//根据题目给的递推
    }
    x1[1]=1;x2[1]=1;
    for(int i=2;i<=1000;++i){			//见上系数递推
        x1[i]=x1[i-1]+x2[i-1];
        x2[i]=x1[i-1];
    }
    while(cas--){
        ll n;
        scanf("%lld",&n);
        if(n==1){
            printf("1\n");				//1测出来是个特例,放这里
            continue;
        }
        ll k=1;
        while(n>100*qpow(2,k))k++;		//算合适的k
        printf("%lld\n",x1[k]*cnt[n/qpow(2,k)]+x2[k]*cnt[n/qpow(2,k+1)]);	//见上公式带入
        //printf("%lld  %lld\n",x1[k]*cnt[n/qpow(2,k)]+x2[k]*cnt[n/qpow(2,k+1)],cnt[n]);
    }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值