牛客练习赛8

问题描述:

A

给个n,求1n的所有数的约数个数的和~

输入描述:

第一行一个正整数n

输出描述:

输出一个整数,表示答案

输入

3

输出

5

n <= 100000000

题目分析:遍历1到n,那么每一个数i对因子的贡献为n/i个

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;

int main()
{
    ll n,m;
    while (scanf("%lld",&n)!=EOF) {
        ll ans=0;
        m=n/2;
        for (ll i=1;i<=(m+1);i++) {//很明显大于m之后都是1
            ans+=n/i;
        }
        ans+=m;
        if (m*2==n) ans--;//记得去重
        printf("%lld\n",ans);
    }
    return 0;
}

B

 一个数轴,每一个储物点会有一些东西,同时它们之间存在距离。
每次给个区间[l,r],查询把这个区间内所有储物点的东西运到另外一个储物点的代价是多少?
比如储物点i有x个东西,要运到储物点j,代价为x * dist( i , j )
dist就是储物点间的距离。 

输入描述:

第一行两个数表示n,m

第二行n-1个数,第i个数表示第i个储物点与第i+1个储物点的距离ai

第三行n个数,表示每个储物点的东西个数bi

之后m行每行三个数x l r

表示查询要把区间[l,r]储物点的物品全部运到储物点x的花费
每次查询独立

输出描述:

对于每个询问输出一个数表示答案
答案对1000000007取模
对于100%的数据n,m <= 200000 , 0 <= a i ,b i <= 2000000000

题目分析:利用前缀和来求区间问题.

当x<=l时 区间[l,r]到x的代价等于区间[l,r]到1的代价,减去重量[l,r]从1到x的代价

当x>=r时 区间[l,r]到x的代价等于区间[l,r]到n的代价,减去重量[l,r]从n到x的代价

其他 区间[l,r]到x的代价分成俩份[l,x-1]到x和[x+1,r]到x

其中[l,r]到1或者到n的代价很简单。

代码如下

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;

const ll mod=1e9+7;
const int maxn=2*1e5+100;
ll dis[maxn],Left[maxn],Right[maxn],b[maxn],sum_b[maxn],cost_l[maxn],cost_r[maxn];
int main()
{
    int n,m;
    while (scanf("%d%d",&n,&m)!=EOF) {
        Left[1]=0;
        for (int i=2;i<=n;i++) {
            scanf("%lld",&dis[i]);
            Left[i]=(Left[i-1]+dis[i])%mod;//左距离
        }
        Right[n]=0;
        for (int i=n-1;i>=1;i--) {
            Right[i]=(Right[i+1]+dis[i+1])%mod;//右距离
        }
        for (int i=1;i<=n;i++) {
            scanf("%lld",&b[i]);
            sum_b[i]=(sum_b[i-1]+b[i])%mod;//重量前缀和
        }
        cost_l[1]=0;
        for (int i=2;i<=n;i++) {
            cost_l[i]=(cost_l[i-1]+b[i]*Left[i])%mod;//前i个物品到1的代价
        }
        cost_r[n]=0;
        for (int i=n-1;i>=1;i--) {
            cost_r[i]=(cost_r[i+1]+b[i]*Right[i])%mod;//前i个物品到n的代价
        }
        ll x,l,r,ans;
        for (int i=1;i<=m;i++) {
            scanf("%lld%lld%lld",&x,&l,&r);
            ans=0;
            if (l>=x) {
                ans=(cost_l[r]-cost_l[l-1]-(sum_b[r]-sum_b[l-1])*Left[x]+mod)%mod;
            }
            else if (r<=x) {
                ans=(cost_r[l]-cost_r[r+1]-(sum_b[r]-sum_b[l-1])*Right[x]+mod)%mod;
            }
            else {
                ans=(cost_r[l]-cost_r[x]-(sum_b[x-1]-sum_b[l-1])*Right[x]+mod)%mod;
                ans=(ans+(cost_l[r]-cost_l[x]-(sum_b[r]-sum_b[x])*Left[x]+mod)%mod+mod)%mod;
            }
            printf("%lld\n",(ans+mod)%mod);
        }
    }
    return 0;
}

D

给你一个 个点,条边的无向图,求至少要在这个的基础上加多少条无向边使得任意两个点可达

输入描述:

第一行两个正整数 n 和 m 。
接下来的m行中,每行两个正整数 i 、 j ,表示点i与点j之间有一条无向道路。

输出描述:

输出一个整数,表示答案
对于100%的数据,有n,m<=100000。
题目分析:直接并查集有几个联通快就需要n-1条路

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;

const int maxn=1e5+100;
int pre[maxn];
int n,m;
vector<int> vec;
void init()
{
    for (int i=1;i<=n;i++) {
        pre[i]=i;
    }
}
int find(int x)
{
    if (pre[x]==x) return pre[x];
    else {
        int ans=find(pre[x]);
        return pre[x]=ans;
    }
}
void join(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if (fx!=fy)
        pre[fx]=fy;
}
int main()
{
    while (scanf("%d%d",&n,&m)!=EOF) {
        init();
        for (int i=1;i<=m;i++) {
            int a,b;
            scanf("%d%d",&a,&b);
            join(a,b);
           // join(b,a);
        }
        for (int i=1;i<=n;i++) {
            join(i,pre[i]);
        }
        vec.clear();
        for (int i=1;i<=n;i++) {
            if (vec.size()==0) vec.push_back(pre[i]);
            else {
                bool flag=false;
                for (int j=0;j<vec.size();j++) {
                    if (vec[j]==pre[i]) {
                        flag=true;
                        break;
                    }
                }
                if (!flag) vec.push_back(pre[i]);
            }
        }
        printf("%d\n",vec.size()-1);
    }
    return 0;
}
E

给出一个集合和一个数m。

集合里面有n个质数。

请你求出从 1 到 m 的所有数中,至少能被集合中的一个数整除的数的个数。

输入描述:

第一行两个正整数 n 和 m 。
第二行n个正整数,分别为集合中的质数。

输出描述:

输出一个整数,表示符合要求的正整数的个数。
对于100%的数据,有n<=20,m为有符号64位正整数,集合内质数<=1000000000
题目分析:简单容斥就好了。

代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll unsigned long long
using namespace std;

ll prime[25],ans,m;
int n;

void dfs(ll a,int cur,int cnt)
{
    if (a>m) return ;
    if (cnt&1)
        ans+=m/a;
    else
        ans-=m/a;
    for (int i=cur+1;i<n;i++) {
        dfs(prime[i]*a,i,cnt+1);
    }
}
int main()
{
    while (scanf("%d%lld",&n,&m)!=EOF) {
        for (int i=0;i<n;i++) {
            scanf("%lld",&prime[i]);
        }
        ans=0;
        for (int i=0;i<n;i++) {
            dfs(prime[i],i,1);
        }
        printf("%lld\n",ans);
    }
    return 0;
}


























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值