牛客练习赛25

A 因数个数和

链接:https://www.nowcoder.com/acm/contest/158/A
来源:牛客网
 

题目描述

q次询问,每次给一个x,问1到x的因数个数的和。

输入描述:

第一行一个正整数q ;
接下来q行,每行一个正整数 x

输出描述:

共q行,每行一个正整数表示答案

示例1

输入

复制

4
1
2
3
10

输出

复制

1
3
5
27

说明

1的因数有1

2的因数有1,2

3的因数有1,3

以此类推

备注:

1<=q<=10 ,1<= x<=109

这个题有两种方法做。

我们现在例如n=12.

1:1  
2:1,2 
3:1,3 
4:1,2,4 
5:1,5 
6:1,2,3,6 
7:1,7 
8:1,2,4,8 
9:1,3,9 
10:1,2,5,10 
11:1,11 
12:1,2,3,4,6,12 

首先第一种方法:我们可以看到求因子个数,其实就是求因子出现的次数。通过上面因子数可以看出,1出现12次,2出现5次,3出现4次……。我们可以观察到因子数的和就是 n/1+n/2+n/3……+n/n。这是一个y=n/x的函数。可以在草稿纸上画出图形,图形是关于y=x对称的。并且对称点在sqrt(n)处,然后我们求处这个函数1到n的离散值就行了,求和相当于求面积,因为对称点在sqrt(n)处,所以我们只用求到sqrt(n)处再乘以2,但是这样有加重复的,所以我们要减去中间加重复的矩形,具体思想看代码。

扫描二维码关注公众号,回复: 2957244 查看本文章
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e4+10;
const int mod=1e9+7;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int main()
{
    int t;cin>>t;
    while(t--)
    {
        int n;scanf("%d",&n);
        ll sum=0;
        for(int i=1;i*i<=n;i++)
            sum+=n/i;
        ll temp=sqrt(n);
        printf("%lld\n",sum*2-temp*temp);
    }
    return 0;
}

第二种方法是利用算数的基本原理,我们设某个数的因子数的函数为T(x)。所以 ,ans=T(1)+T(2)+……+T(12)。然后我们看每个函数是有规律可找的。

ans=12∗1+6∗2+4∗3+3∗4+2∗5+2∗6+1∗7+1∗8+1∗9+1∗10+1∗11+1∗12。ans=∑ni=1n/i∗i。(n/i)是要整除。

可以整理得:ans=12∗1+6∗2+4∗3+3∗4+2∗(5+6)+1∗(7+8+9+10+11+12)。

则对于每一个 n/i 都有一个范围

n/i 范围[l,r] 
当前n/i 在范围内 对ans的贡献是 
12 [1,1] 12*1 
6 [2,2] 6*2 
4 [3,3] 4*3 
3 [4,4] 3*4 
2 [5,6] 2*(5+6) 
1 [7,12] 1*(7+8+……+12) 然后这里可以等差数列求和

可以发现 每一个l等于上一个r+1 而r=n/(n/l)这里是整除

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL X(LL n)
{
    LL l, r;
    LL ans = 0;
    for( l = 1; l <= n; l = r+1)
    {
        r = n/(n/l);
        ans += n/l * (r-l + 1);
        //(r-l+1)     项数 
        //(n/l) 是拥有这样因子的区间个数 
    }
    return ans;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        LL x;
        cin>> x;
        printf("%lld\n",X(x));
    }
}

B 最长区间

链接:https://www.nowcoder.com/acm/contest/158/B
来源:牛客网
 

题目描述

给你一个长度为 n 的序列 a ,求最长的连续的严格上升区间的长度。
同时会进行 m 次修改,给定 x , y ,表示将 ax 修改为 y ,每次修改之后都要求输出答案。

输入描述:

第一行 2 个数 n,m,表示序列长度,修改次数; 
接下来一行 n 个数表示  ;
接下来 m 行,每行 2 个数 x , y ,描述一次修改。

输出描述:

第一行 1 个数表示最初的答案;
接下来 m 行,第 i 行 1 个数表示第 i 次修改后的答案。

示例1

输入

复制

4 3
1 2 3 4
3 1
2 5
3 7

输出

复制

4
2
2
3

说明

序列变换如下:
1  2  3  4
1  2  1  4
1  5  1  4
1  5  7  4

备注:

n,m ≤ 100000,1 ≤ x ≤ n,1 ≤ ai,y ≤ 100

PS:听说暴力能过,但是我这里写的是线段树,大家可以参考下。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>
const int maxn=1e5+5;
const int mod=1e9+7;
#define me(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int a[maxn],num[maxn<<2],lnum[maxn<<2],rnum[maxn<<2];
void updata(int l,int r,int rt)///更新区间最长子序列长度
{
   int m=(l+r)/2;
    lnum[rt]=lnum[rt<<1];
    rnum[rt]=rnum[rt<<1|1];
    num[rt]=max(num[rt<<1],num[rt<<1|1]);
    if(a[m]<a[m+1])
    {
        if(lnum[rt<<1]==m-l+1)
            lnum[rt]=lnum[rt<<1]+lnum[rt<<1|1];
        if(rnum[rt<<1|1]==r-m)
            rnum[rt]=rnum[rt<<1|1]+rnum[rt<<1];
        num[rt]=max(num[rt],lnum[rt<<1|1]+rnum[rt<<1]);
    }
}
void build(int l,int r,int rt)///建树
{
    if(l==r)
    {
        num[rt]=lnum[rt]=rnum[rt]=1;
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    updata(l,r,rt);
}
void pushdata(int s,int c,int l,int r,int rt)///更改节点值
{
    if(l==r)
    {
        a[l]=c;
        return ;
    }
    int m=(l+r)>>1;
    if(s<=m)
        pushdata(s,c,l,m,rt<<1);
    else
        pushdata(s,c,m+1,r,rt<<1|1);
    updata(l,r,rt);
}
int query(int L,int R,int l,int r,int rt)///求出最长子序列长度
{
     if(l>=L&&R>=r)
        return num[rt];
    int ret=0;
    int m=(l+r)/2;
    if(m>=L)
        ret=max(ret,query(L,R,l,m,rt<<1));
    if(R>m)
        ret=max(ret,query(L,R,m+1,r,rt<<1|1));
    if(a[m]<a[m+1])
        ret=max(ret,min(m-L+1,rnum[rt<<1])+min(R-m,lnum[rt<<1|1]));///[m+1,R]与[m+1,r]相交部分的最大前缀+[L,m]与[l,m]的最大后缀.
    return ret;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    build(1,n,1);
    cout<<query(1,n,1,n,1)<<endl;
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        pushdata(a,b,1,n,1);
        printf("%d\n",query(1,n,1,n,1));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41292370/article/details/82180338