miniEye2021训练第一天题解

01.Mod, Or and Everything

这个刚开始暴力超时了,后来打印出前一百项,发现是按2倍数增加的。列出通项公式即可。设 a n = ( n m o d 1 ) o r ( n m o d 2 ) o r . . . o r ( n m o d ( n − 1 ) ) o r ( n m o d n ) a_n= (n mod 1) or (n mod 2) or ... or (n mod (n - 1)) or (n mod n) an=(nmod1)or(nmod2)or...or(nmod(n1))or(nmodn),则有a(n)->pow(2,ceil(log2(n)) - 1) - 1,不过要注意int会溢出,换成long long就过了。

考完了一想,按位取或就能把大部分位取为1,验证范围就能发现是所有位都是1,结果就是2^k。不过这个k还要提另求一下。官方题解用的是这个思路,不过不太容易想到,没有观察来的快。是属于降维打击的解法。

我的代码:

#include<cstdio>
#include<iostream>
#include<cmath>

using namespace std;

int calculate(int n){
    if(n == 1)return 0;
    return pow(2,ceil(log2(n)) - 1) - 1;
}

long long calculate1(int b){
    long long temp=4;
    if(b <= 2)return 0;
    while(temp<b)
    {
        temp=temp*2;
    }
    return temp/2-1;
}

int main()
{
    int T,n;
    cin >> T;
    while(T--){
        // cin >> n;
        n = T;
        if(calculate(n) != calculate1(n) )cout << n << " " << calculate(n) << " " << calculate1(n) << endl;
    }
}

官方思路及代码:

当n为偶数时,设m=n/2-1
当n为奇数时,设m=(n-1)/2
可以发现,n mod i<=m,且当i<=m时,有 n mod (n-i)=i。于是可以得出n mod i取到0~m的所有整数,因此答案会是2^k-1,k的具体值判断一下即可
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL T,n;
int main()
{
	scanf("%lld",&T);
	while(T--){
		scanf("%lld",&n);
		if((n&(-n))==n){
			printf("%lld\n",max(0ll,n/2-1));
			continue;
		}
		while((n&(-n))!=n)n-=(n&(-n));
		printf("%lld\n",n-1);
	}
}

05.Minimum spanning tree

这个题我推出来的利用不同大小的树之间的递推关系,大树减小树是lcm(n, i)序列的最小值。但这样超时了,主要原因是题目限制了复杂度为O(n)?因为我计算min{lcm(n, i)}时复杂度有点高,本来以为复杂度能优化到i=2,但发现并不是,看了题解才知道要和数向因数靠拢,i=2只是质数的情况。(i=2恰是n=6时的特例,做题的时候还是例子找的小了,也没有自己推导导致的错误。)

后来想了想,只要把min{lcm(n, i)}的复杂度降下来就可以了。可以采用官方的处理方式,使用欧拉筛,计算优化为
m i n { l c m ( n , i ) } = 合 数 取 因 , 质 数 乘 2 min\{lcm(n, i)\} = 合数取因,质数乘2 min{lcm(n,i)}=2即可。

官方思路及代码:

对于编号为3~n的点,将所有编号为合数的点向其约数连边,编号为质数的点向2连边,不难证明这样形成的生成树是最小的。
总的边权和为(质数的和*2+合数的和),用欧拉筛预处理前缀和即可。
效率:O(n)
#include<bits/stdc++.h>
#define rep(i,x,y) for(auto i=(x);i<=(y);++i)
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int M=1e7+20;
bool v[M];
int tot,p[N];//p是质数列
ll a[N];
void sol(){int n;
	scanf("%d",&n);
	printf("%lld\n",ll(3+n)*(n-2)/2+a[upper_bound(p+1,p+tot+1,n)-p-1]);
}
int main(){
	int t;
	rep(i,2,M-1){
		if(!v[i])p[++tot]=i;
		rep(j,1,tot){
			if(i*p[j]>=M)break;
			v[i*p[j]]=1;
			if(!(i%p[j]))break;
		}
	}
	//以上是欧拉筛
	rep(i,2,tot)a[i]=p[i]+a[i-1];//质数和
	scanf("%d",&t);
	rep(i,1,t)sol();	
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值