8.7 补题

E AND

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

输入描述:

第一行给出一个数t
之后 t 行,每行会给出两个正整数 x和y。

1≤t≤150
2≤x≤​y≤100000000

输出描述:

每行输出两个整数a,b
a是x和y区间内的所有质数的数目
b是x和y区间内的所有质数形成的数组中有多少子区间and和为0

示例1

输入

3
2 3
2 4
2 5

输出

2 0
2 0
3 1

说明

第一个的质数有2,3

第二个的质数有2,3

第三个的质数有2,3,5,并且2,3,5的and和为0

思路:

代码:

欧拉筛 二分查找

#include <bits/stdc++.h>
#define int long long
using namespace std;
bool a[100000010];
vector<int>b;
void ols(int n){
	a[1]=1;
	for(int i=2;i<=n;i++){
		if(!a[i])b.push_back(i);
		for(int j:b){
			if(j*i>n)break;
			a[j*i]=1;
			if(i%j==0)break;
		}
	}
}
signed main(){
	ols(100000000);
	int t;
	cin>>t;
	while(t--){
		int x,y;
		cin>>x>>y;
		int sum=0,l1=0,r1=b.size()-1,l2=0,r2=r1;
		/*while(l1<r1){
			int m=l1+r1>>1;
			if(b[m]<x)l1=m+1;
			else r1=m;
		}
		while(l2<r2){
			int m=l2+r2>>1;
			if(b[m]<y)l2=m+1;
            else r2=m;
		}*/
        int l=lower_bound(b.begin(), b.end(),x)-b.begin();
        int r=upper_bound(b.begin(), b.end(),y)-b.begin();
		//if(b[r]>y)r--;
        //sum=l2-l1+1;
        sum=r-l;
		cout<<sum<<' ';
		if(x>2||sum==1)cout<<0<<'\n';
		else if(sum>=2)cout<<sum-2<<endl;
	}
	return 0;
}

I 马拉松 

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

    小明生活在一个名为running的国家,该国家由 n 个城市组成,编号从 1 到 n,还有 n−1 条双向道路连接着这些城市。从任何一个城市都可以到达另一个城市。每条道路都连接着两个城市 a 和 b 。这个国家是一个热爱跑步的国家,因此每天都会在一对城市(u ,v) 之间举办一次马拉松,从 u 作为起点, v 作为终点,且 u != v 。小明需要用最短的路径到达从 u 到达 v(注意 (u ,v) 和 (v ,u)是不同的马拉松)。
令小明头疼的是该国家有两个城市 x 和 y,当小明经过 x 城市后又跑到 y 城市,那么该城市的警察就会阻止小明继续参加比赛。
小明想知道在举办的所有马拉松路线中他会有多少个马拉松会在他参加时会被警察阻止。

输入描述:

第一行包含三个整数 n 、 x 和 y --分别是城市数量、x 城市的编号和 y 城镇的编号。
接下来是 n−1 行,每行包含两个整数 a 和 b,描述了连接两个城镇 a 和 b 的道路。
可以保证的是,从每个城镇出发,我们都可以通过给定的道路到达城市中的其他城镇。也就是说,给定的城镇和道路地图是一棵树。

输出描述:

一个整数,表示小明在参加马拉松时会被警察阻止的数量 (u,v) 。

示例1

输入

5 1 5
1 2
2 3
3 4
4 5

输出

1

示例2

输入

5 1 5
1 2
1 3
1 4
1 5

输出

4

示例3

输入

5 2 3
1 2
1 3
2 4
1 5

输出

2

思路:

找y的子节点不会经过x的数量,x的字节点数相乘。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,x,y,u,v,sun[300005]; //sun[]记录该节点的子节点个数 
vector<int>edge[300005];
bool st[300005],check[300005];
int dfs(int u){
	st[u]=1;//是否走过 
	sun[u]=1;
	if(u==x){
		check[u]=1;
	}
	for(int i:edge[u]){
		if(!st[i]){
			sun[u]+=dfs(i);
			if(check[i]){
				check[u]=check[i];
			}
		}
	}
	return sun[u];
}
signed main()
{
    cin>>n>>x>>y;
    for(int i=0;i<n-1;i++){
    	cin>>u>>v;
    	edge[u].push_back(v);
    	edge[v].push_back(u);
	}
	dfs(y);
	int a=0;
	for(int i:edge[y]){
		if(check[i]==1){//表示会经过x的 
			a=sun[y]-sun[i];
		}
	}
	cout<<a*sun[x]<<endl;
    return 0;
}

 

J 尖塔第四强的高手

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

机宝是《杀戮尖塔》中第四强的高手,他掌握着大量随机数,擅长跳跃世界线。今天他遇到了一个精英怪,在一番苦战后他决定跳跃世界线,但由于他的集中掉完了,因此需要你帮助他跳跃世界线。
世界线可以表示为一棵 n 个结点的有根树,结点编号从 1 到 n,对于其中任意一棵子树,该子树的根节点可以到达该子树内任意一点(包括子树根节点自己)。
设数列 F1=1,F2=2,Fi=Fi−1+Fi−2(i≥3)机宝有 q 次询问,每次询问他会给出两个正整数 x,k 生成点集 {x+Fi∣i∈N,i≥k,x+Fi≤n},机宝想找一个结点,使得该结点能到达点集中任意一点,并且在所有符合要求的结点中,该结点距离根节点最远。

输入描述:

第一行包含三个正整数 n,r,q,分别表示树的结点数量、根节点编号和询问次数。 
接下来 n−1 行每行包含两个正整数 u,v,表示编号为 u 的结点和编号为 v 的结点之间有一条边(数据保证可以构成树)。 
接下来 q 行每行包含两个正整数 x,k,含义同题目描述。 

输出描述:

输出共 q 行,每行一个整数,表示符合题目要求的结点编号。如果不存在符合题目要求的结点,输出 0。

示例1

输入

6 1 3
1 2
1 3
2 4
2 5
4 6
4 1
1 4
6 1

输出

2
6
0

说明

第一次询问的点集为 {5,6},结点 1 和结点 2 都能到达点集中任意一点,结点 2 距离根节点最远,答案是 2。 
第二次询问的点集为 {6},答案是 6。 
第三次询问为空集,答案是 0。

示例2

输入

10 7 4
1 2
1 3
2 6
3 4
4 5
4 7
5 9
6 10
7 8
2 5
4 3
7 3
7 2

输出

10
7
10
4

思路:

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,r,q;
vector<int>edge[100005];
int f[100005][20],dep[100005],F[26];
void dfs(int x,int FA)
{
    dep[x]=dep[FA]+1;f[x][0]=FA;
    for(auto y:edge[x])
        if(y!=FA)  dfs(y,x);
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])  swap(x,y);
    for(int i=17;i>=0;--i)
        if(dep[f[x][i]]>=dep[y])  x=f[x][i];
    if(x==y)  return x;
    for(int i=17;i>=0;--i)
        if(f[x][i]!=f[y][i])  x=f[x][i],y=f[y][i];
    return f[x][0];
}
signed main()
{
    cin>>n>>r>>q;
    F[1]=1;F[2]=2; 
    for(int i=3;i<=25;++i){
		F[i]=F[i-1]+F[i-2];
	}
    for(int i=1;i<n;++i) {
        int x,y;
        cin>>x>>y;
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    dfs(r,0);
    for(int j=1;j<=17;++j)
        for(int i=1;i<=n;++i)
            f[i][j]=f[f[i][j-1]][j-1];
    while(q--){
        int x,k;
        cin>>x>>k;
        if(k>=25||x+F[k]>n)  cout<<0<<endl;
        else {
            int ans=0;
            for(int i=k;x+F[i]<=n;i++) {
                if(!ans)  ans=x+F[i];
                else ans=LCA(ans,x+F[i]);
            }
            cout<<ans<<endl;
        }
	}
    return 0;
}

 

K 比赛

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

题目描述

神圣的帝皇禁军作为战锤40k中帝皇的亲卫,拥有无与伦比的力量。某一天,禁军们决定举办一场比赛,来活动一下他们的身体。每一个禁军都有一个能力值a[i](他们的实力可能相等),他们的比赛将会有两个神圣禁军作为参赛选手,但是禁军的power实在是太强大了,所以他们必须选择一个实力在他们两个人之间的神圣禁军来维护秩序。如果这个神圣禁军实力比参赛选手都强大或者比他们都弱小,那么两个选手都将会非常不满意,所以你必须保证维护秩序的禁军战士的实力在两个神圣禁军战士之间才可以。每三个神圣禁军只会进行一场比赛,要求每场比赛中aj∈[min(ai​,ak​),max(ai​,ak​)],(1≤i<j<k≤n)。请问聪明的你能计算出来一共能有多少场比赛吗?

输入描述:

第一行输入为T代表有多个测试点。(1≤T≤40)

接下来每两行一个测试点。

第一行输入为N,表示参与比赛的禁军数量。(3≤N≤20000)

第二行包含N个输入,ai表示第iii个禁军的能力值。(1≤ai≤100000)

输出描述:

每个测试点输出一个数,代表最多能有多少场比赛。

示例1

输入

1
6
8 4 11 14 13 1

输出

6

示例2

输入

2
4
12 14 6 10
11
1 14 3 2 11 9 4 9 4 14 12

输出

0
78

示例3

输入

1
7
5 4 8 4 6 1 10

输出

13

示例4

输入

1
4
1 1 1 1

输出

4

思路:

找一个数a[i]左边有几个小于等于a[i]的数乘上右边有几个大于等于a[i]的数 

找一个数a[i]左边有几个大于等于a[i]的数乘上右边有几个小于等于a[i]的数 

找到与a[i]相同的数,减去相同的数,因为1,2求了两遍

再用树状数组

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
int T,n,a[20005],tree[100005];
int llow[20010],lup[20010],rlow[20010],rup[20010],lcnt[20010],rcnt[20010];
int lowbit(int x){
	return x&(-x);//二进制位最后一个1的位置的值 
}
void update(int x, int k){
    while(x<=100000) {
	    tree[x]+= k;
     	x+= lowbit(x);//更新tree[]个数 
    }
}
int query(int p){//求和 个数 
	if(p==0){
		return 0;
	}
	return query(p-lowbit(p))+tree[p];
}
signed main()
{
    cin>>T;
    while(T--){
    	int ans=0;
    	cin>>n;
    	for(int i=1;i<=n;i++){
    		cin>>a[i];
		}
		for(int i=1;i<=n;i++){
			llow[i]=query(a[i]);//左边小于a[i]的数 
            lup[i]=query(100000)-query(a[i]-1);//左边大于a[i]的数  
            lcnt[i] = query(a[i])-query(a[i] - 1);//与a[i]相等的数 
            update(a[i],1);
		}
		for(int i=1;i<=100000;i++){
			tree[i]=0;
		}
		for(int i=n;i>=1;i--){
			rlow[i]=query(a[i]);
            rup[i]=query(100000)-query(a[i]-1);
            rcnt[i] = query(a[i])-query(a[i]-1);
            update(a[i],1);
            ans+=rlow[i]*lup[i];
        	ans+=llow[i]*rup[i];
        	ans-=lcnt[i]*rcnt[i];
		} 
		for(int i=1;i<=100000;i++){
			tree[i]=0;
		}
		cout<<ans<<'\n';
	}
    return 0;
}

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值