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(n−1))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();
}