• [第八场]题解


    T1

    题目描述
    我们如何评价科学家的成功?根据发表论文的数量或其引用分数——更准确地说,被引用的次数?这两个要素都很重要。如果一篇论文被其他科学家的论文总共引用了C次,那么这篇论文的引用分数为C。科学家成功的一个可能衡量标准是他们的H指数,它同时考虑了论文数量和引用分数。 科学家的H指数定义为具有以下性质的最大整数H:科学家可以选择H篇论文,满足这H篇论文的引用分数都不小于H。例如,如果一个科学家写了10篇论文,并且每一篇都被引用了至少10次,那么他的H指数(至少)是10。 写一个程序,输入某个科学家所有论文的引用分数,输出他的H指数。
    输入
    第一行输入包含整数N(1≤N≤5*1e5)。表示论文的数量。 第二行输入包含N个自然数Ci(Ci≤1e6)。表示每篇论文的引用分数。
    输出
    输出一个整数。表示这名科学家的H指数。
    样例
    样例输入1
    5
    1 1 4 8 1
    样例输出1
    2
    样例输入2
    5
    8 5 3 4 10
    样例输出2
    4

    解题思路

    此题比较轻松,和大众做法不同,我并没有排序,我用了一个cnt统计数字出现次数,然后倒着来找符合条件的,即积分为i满足能选i篇积分至少为i的。

    代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<cstdlib>
    using namespace std;
    int n,c,cnt[1000005],maxn;
    int sum;
    int main(){
    	//freopen("hindex.in","r",stdin);
    	//freopen("hindex.out","w",stdout);
    	scanf ("%d",&n);
    	for (int i = 1;i <= n;i ++){
    		scanf ("%d",&c);
    		cnt[c] ++;
    		maxn = max(c,maxn);
    	}
    	for (int i = maxn;i >= 0;i --){
    		sum += cnt[i];
    		if (sum >= i){
    			printf("%d
    ",i);
    			return 0;
    		}
    	}
    }
    

    T2

    题目描述
    一个房间里有N张桌子,从左到右排成一行。有些桌子上有电话,而有些桌子是空的。所有的电话都坏了,所以当第i个办公桌上的电话响了,第j个办公桌上的电话也会响,如果第j个办公桌和第i个办公桌的距离不超过D。换句话说,j和i满足|j-i|≤D。第一张桌子和最后一张桌子上一定会有电话。开始的时候,最左边的桌子上的电话会响起。求要让最后一个电话响起,至少要在多少张办公桌上放置新电话?
    输入
    第一行输入包含两个整数N(1≤N≤3e5)和D(1≤D≤N)。分别表示办公桌的数量和题目中提到的距离。 第二行输入包含了N个整数Ai(0≤Ai≤1)。第i个整数Ai表示第i个张桌子的状态。具体来说,如果Ai=1那么第i张桌子上有电话,如果Ai=0那么第i张桌子上没有电话。
    输出
    输出一个整数。表示需要放置新电话的数量。
    样例
    样例输入1
    4 1
    1 0 1 1
    样例输出1
    1
    样例输入2
    5 2
    1 0 0 0 1
    样例输出2
    1
    样例输入3
    8 2
    1 1 0 0 1 0 0 1
    样例输出3
    2

    解题思路

    感觉就是一道贪心模拟,我们就直接保证放到最远即可,中途有一些电话线的话,我们就重新开始算。

    代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<cstdlib>
    using namespace std;
    int n,d,dis,tot;
    int vis[300005];
    int main(){
    	//freopen("telefoni.in","r",stdin);
    	//freopen("telefoni.out","w",stdout);
    	scanf ("%d%d",&n,&d);
    	for (int i = 1;i <= n;i ++)
    		scanf ("%d",&vis[i]);
    	for (int i = 1;i <= n;i ++){
    		if (vis[i] == 1)
    			dis = 1;
    		else if (dis == d){
    			tot ++;
    			dis = 1;
    		}
    		else 
    			dis ++;
    	}
    	printf("%d",tot);
    }
    

    T3

    题目描述
    年轻的Jozef得到了一个由2的N次方个正整数组成的集合作为礼物。考虑到Jozef经常参加足球锦标赛,他决定为这2的N次方个正整数组织一个锦标赛。 数字锦标赛如下图所示。比赛成对进行,两个数字中较高的一个前进到更高级别。比赛的级别用1到N之间的数字表示,其中最高的级别用数字0表示。
    模拟图
    由于Jozef没有时间组织所有的比赛,他想知道,对于集合中的每个数字,在所有的对阵安排中,能达到的最高级别(数字最小的级别)。
    输入
    第一行输入包含一个整数N(1≤N≤20)。表示题目中提到的N。 第二行输入包含了2的N次方个正整数Ai(Ai≤1e9)。表示集合中2的N次方个正整数。
    输出
    输出一行包含2的N次方个整数。其中第i个数表示按照输入的顺序集合中的第i个正整数能达到的最高级别。
    样例
    样例输入1
    2
    1 4 3 2
    样例输出1
    2 0 1 1
    样例输入2
    4
    5 3 2 6 4 8 7 1 2 4
    3 3 6 4 8 1
    样例输出2
    1 2 2 1 1 0 1 3 2 1
    2 2 1 1 0 3
    样例输入3
    1
    1 1
    样例输出3
    0 0

    解题思路

    先说一下题意吧,可能有人觉得样例不对,毕竟当时我一开始也理解错了。
    简单来说,他就是让你自己安排,求每一个数最远能到哪。
    那我们肯定要排序啊。a的值很大,我们进行离散。
    这里有一点是相同的数也可以晋级。所以怎么算其能到达的最远呢。
    这里呢,可以找一下规律。
    如果i可以到n - 1,那么肯定要有至少2个小于等于i的数。
    如果i可以到n - 2,那么肯定要有至少4个小于等于i的数。
    如果i可以到n - k,那么肯定要有至少2^k个小于等于i的数。
    就这样完了,处理你自己弄一下差不多了。

    代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<cstdlib>
    using namespace std;
    struct node{
    	int x,pos;
    	bool operator < (const node &a)const {
    		return x < a.x;
    	}
    }s[1500005];
    int n,n1,num[1500005],ans[1500005],tot[1500005],sum,cnt;
    int qkpow(int x,int y){
    	int ans = 1;
    	while (y){
    		if (y % 2 == 1)
    			ans = ans * x ;
    		x = x * x;
    		y /= 2;
    	}
    	return ans;
    }
    int main(){
    	//freopen("turnir.in","r",stdin);
    	//freopen("turnir.out","w",stdout);
    	scanf ("%d",&n);
    	n1 = qkpow(2,n);
    	for (int i = 1;i <= n1;i ++){
    		int a;
    		scanf ("%d",&a);
    		s[i].x = a;
    		s[i].pos = i;
    	}
    	sort(s + 1,s + 1 + n1);
    	for (int i = 1;i <= n1;i ++){
    		if (s[i].x != s[i - 1].x){
    			num[s[i].pos] = ++ cnt;
    			tot[cnt] ++;
    		}
    		else {
    			num[s[i].pos] = cnt;
    			tot[cnt] ++;
    		}
    	}
    	for (int i = 1;i <= cnt;i ++){
    		sum += tot[i];
    		int j,tot1 = 0;
    		for (j = 1;j <= n1;j *= 2){
    			if (sum < j)
    				break;
    			tot1 ++;
    		}
    		ans[i] = n - tot1 + 1;
    	}
    	for (int i = 1;i < n1;i ++)
    		printf("%d ",ans[num[i]]);
    	printf("%d
    ",ans[num[n1]]);
    }
    

    T4

    题目描述
    如果一个数等于它的所有因数(小于自身的)之和,那么这个数就是完美的。例如,28是完美的,因为28=1+2+4+7+14。

    基于这个定义,我们数字n的不完美度定为f(n),它等于n与n的所有因数(小于n的)之和的差的绝对值,因此完美数的不完全度为0,其余自然数的不完全度都大于0。
    例如:

    ●f(6)=|6-(1+2+3)|=0
    ●f(11)=|11-(1)|=10
    ●f(24)=|24-(1+2+3+4+6+8+12)|=|-12|=12

    写一个程序,对于正整数A和B,计算A和B之间所有数字的不完美度之和:即f(A)+f(A+1)+…+f(B)。
    输入
    第一行输入包含两个整数A和B(1≤A≤B≤1e7)。表示题目中的A和B。
    输出
    输出一个整数。表示f(A)+f(A+1)+…+f(B)。
    样例
    样例输入1
    1 9
    样例输出1
    21
    样例输入2
    24 24
    样例输出2
    12

    解题思路

    其实这是一道求约数和的模板题,不完美度这玩意你求出约数和你就可以O(1)时间内求解。
    约数和应该来说是有两种求法,第一种便是nlog(n)的求法,代码简短。第二种则是欧拉筛法的求法,代码略长一点,时间复杂度为O(n)。
    但考试时限3s,好像nlog(n)并不会爆,评测也过了。但OJ上TLE了,所以还是用欧拉筛吧

    代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<cstdlib>
    using namespace std;
    int a,b,psum[10000005],cnt,pri[1500005],s[10000005];
    bool vis[10000005];
    long long ans;
    int main(){
    	scanf ("%d%d",&a,&b);
    	/*约数个数
    	for (int i = 2;i <= b;i ++){
    		if (!vis[i]){
    			pri[++ cnt] = i;
    			num[i] = 1;
    			d[i] = 2;
    		}
    		for (int j = 1;j <= cnt && 1ll  * i * pri[j] <= b;j ++){
    			vis[pri[j] * i] = 1;
    			if (i % pri[j] == 0){
    				num[pri[j] * i] = num[i] + 1;
    				d[pri[j] * i] = d[i] / (num[i] + 1) * (num[i] + 2);
    				break;
    			}
    			d[pri[j] * i] = d[i] * 2;
    			num[i] = 1;
    		}
    	}*/
    	s[1] = 1;
    	for(int i = 2;i <= b;i ++){
    		if (!vis[i]){
    			pri[++ cnt] = i;
    			s[i] = i + 1;
    			psum[i] = i + 1;
    		}
    		for (int j = 1;j <= cnt && 1ll * i * pri[j] <= b;j ++){
    			vis[pri[j] * i] = 1;
    			if (i % pri[j] == 0){
    				psum[pri[j] * i] = psum[i] * pri[j] + 1;
    				s[pri[j] * i] = s[i] / psum[i] * psum[pri[j] * i];
    				break;
    			}
    			s[pri[j] * i] = s[i] * (pri[j] + 1);
    			psum[pri[j] * i] = pri[j] + 1;
    		}
    	}
    	for (int i = a;i <= b;i ++)
    		ans += abs(i * 2 - s[i]);
    	printf("%lld
    ",ans);
    }
    

    T5

    题目描述
    Daniel有一袋糖果和N张卡片。

    每张卡片上都写有一个正整数Pi。丹尼尔在吃糖果的时候,想到了一个有趣的游戏。Daniel可以挑选两张卡片,将它们绑在一起,如果Daniel挑选第a张牌和第b张牌,那么Daniel会马上吃掉min(Pa%Pb,Pb%Pa)个糖果,其中x%y表示x除以y的余数。

    Daniel每次会挑选两张卡片将它们绑在一起,最终,当他提起其中一张卡片时,所有其余的卡片也会被提起。他可以多次将同一张卡片和其他卡片绑在一起。Daniel要求你计算他至少要吃多少糖果。
    输入
    第一行输入包含一个整数N(1≤N≤1e5)。表示卡片的数量。

    接下来N行每行包含一个正整数Pi(Pi≤1e7)。第i个整数Pi表示第i张卡片上的正整数。
    输出
    输出一个整数。表示Daniel至少要吃多少糖果。
    样例
    样例输入1
    4
    2
    6
    3
    11
    样例输出1
    1
    样例输入2
    4
    1
    2
    3
    4
    样例输出2
    0
    样例输入3
    3
    4
    9
    15
    样例输出3
    4

    解题思路

    一看也知道是一道最小生成树。然而数据太大了,用裸的最小生成树跑不仅会超时还会爆空间,可怕可怕。
    正解其实就是最小生成树加优化。
    首先,如果存在倍数关系,连边的花费就为0,我们可以缩点,但有一点要注意。不能够强行把这个点删去,我们把它放在一个集合中即可。强行删去会使得答案有误,其它点有可能与另一个点的倍数连边。列如3,11,22.
    然后就是删边,我们要保存有意义的边。一些肯定用不上的边一定要删去。
    我们定义一个数组q,q[i]则表示大于等于i的最小的数(原数列出现过)。
    处理到一个点我们便可以枚举这一个值的倍数,至于那三个数的推导,给个链接但其实还有一个问题,那就是枚举倍数时这样加会有重复的边出现,我们仅仅需要存一个,就需要判断一下,看下一次塞进去的是不是与此相等。还有一点是,先处理判断一下q[p[i] + 1],然后再从2*p[i]开始枚举。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #define P 10000005
    #include<cstdlib>
    using namespace std;
    struct node {
    	int u,v,w;
    	node (){};
    	node (int U,int V,int W){
    		u = U,v = V,w = W;
    	}
    	bool operator < (const node &a)const {
    		return w < a.w;
    	}
    };
    int n,p[P],cnt,fa[P],q[P],flag[P],maxn,a[P],k,n1;
    bool vis[P];
    vector <node>s;
    void makeset(int x){
    	for (int i = 1;i <= x;i ++)
    		fa[i] = i;
    }
    int findset(int x){
    	if (fa[x] != x)
    		fa[x] = findset(fa[x]);
    	return fa[x];
    }
    int main(){
    	scanf ("%d",&n);
    	for (int i = 1;i <= n;i ++){
    		scanf ("%d",&p[i]);
    		maxn = max(p[i],maxn);
    		q[p[i]] = p[i];
    		flag[p[i]] = i;
    	}
    	makeset(maxn);
    	for (int i = maxn;i >= 0;i --)
    		if (!q[i])
    			q[i] = q[i + 1];
    	for (int i = 1;i <= n;i ++){
    		for (int j = p[i] * 2;j <= maxn;j += p[i]){
    			if (flag[j]){
    				int t1 = findset(p[i]);
    				int t2 = findset(j);
    				if (t1 != t2){
    					fa[t2] = t1;
    					k ++;
    				}
    			}
    		}
    	}
    	for (int i = 1;i <= n;i ++){
    		if (!vis[p[i]]){
    			vis[p[i]] = 1;
    			int t1 = findset(p[i]);
    			if (p[i] + 1 > maxn || (q[p[i] + 1] != q[p[i] + p[i]] && findset(q[p[i] + 1]) != t1))
    				s.push_back(node(p[i],q[p[i] + 1],q[p[i] + 1] % p[i]));
    			for (int j = p[i] * 2;j <= maxn;j += p[i]){
    				if (j + p[i] > maxn || (q[j] != q[j + p[i]] && findset(q[j]) != t1)){
    					s.push_back(node(p[i],q[j],q[j] % p[i]));
    				}
    			}
    		}
    		else 
    			n1 ++;
    	}
    	sort(s.begin(),s.end());
    	long long mst = 0;
    	for (int i = 0;i < s.size() && k < n - n1 - 1;i ++){
    		int t1 = findset(s[i].u);
    		int t2 = findset(s[i].v);
    		if (t1 != t2){
    			if (t1 < t2)
    				fa[t2] = t1;
    			else 
    				fa[t1] = t2;
    			mst += s[i].w;
    			k++;
    		}
    	}
    	printf("%lld
    ",mst);
    }
    
  • 相关阅读:
    [LUOGU] P2196 挖地雷
    [LUOGU] P1020 导弹拦截
    [LUOGU] P2543 [AHOI2004]奇怪的字符串
    [LUOGU] P2759 奇怪的函数
    [LUOGU] P1048 采药
    [LUOGU] P1396 营救
    [LUOGU] P1196 [NOI2002]银河英雄传说
    [LUOGU] 2820 局域网
    知识点
    swich使用
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/11250071.html
Copyright © 2020-2023  润新知