A: 最小差值
暴力求解。
B:Tree IV
这个题采用模拟的方式去计算会超时。这就利用了二叉树的统计性质,设根结点的深度是1,那么每行的第一个结点的编号值是 2 d e p − 1 2^{dep-1} 2dep−1,最后一个结点的编号值是 2 d e p − 1 2^{dep}-1 2dep−1。这样就可以算出答案。
C:牛牛组数
n n n个数,每个数只能用一次,要求要分成 k k k个数,使得和最大。很明显,先把 n n n个数按从小到大的顺序排序,前 k − 1 k-1 k−1个数分别占前面 n − 1 n-1 n−1个数的一个,最后 n − k + 1 n-k+1 n−k+1个数构成最后一个数。因为 n n n的范围很大,所以要用高精度(只有加法)。
D:牛牛算题
除法分块问题。
【除法分块】
基本思路:维护两个变量 L , R L,R L,R,代表当前的除数区间是 [ L , R ] [L,R] [L,R],在这样的一个闭区间中,满足商是一个定值。
初始情况: L = 1 L=1 L=1,我们在 L ≤ N L\le N L≤N时循环进行:设 t = ⌊ N L ⌋ t=\big \lfloor \frac{N}{L} \big \rfloor t=⌊LN⌋,则当前答案区间的右端点是 R = ⌊ N t ⌋ R=\big \lfloor \frac{N}{t} \big \rfloor R=⌊tN⌋,那么下一个答案区间的左端点就是 L = R + 1 L=R+1 L=R+1.
例题1:P2261 [CQOI2007]余数求和
典型的余数求和问题。我们知道 a % b = a − b ∗ ⌊ a b ⌋ a\%b=a-b*\big\lfloor\frac{a}{b}\big\rfloor a%b=a−b∗⌊ba⌋.因此对于原式有:
∑ i = 1 n k m o d i = ∑ i = 1 n k − i ∗ ⌊ k i ⌋ = n ∗ k − ∑ i = 1 n i ∗ ⌊ k i ⌋ \sum_{i=1}^n k \bmod i=\sum_{i=1}^nk-i*\big\lfloor\frac{k}{i}\big\rfloor=n*k-\sum_{i=1}^n i*\big \lfloor \frac{k}{i} \big \rfloor i=1∑nkmodi=i=1∑nk−i∗⌊ik⌋=n∗k−i=1∑ni∗⌊ik⌋
#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
int main()
{
close;ll n,k;cin>>n>>k;
ll bound=min(n,k),ans=0;
for(ll L=1,R;L<=bound;L=R+1)
{
ll t=k/L;R=min(k/t,n);
ans+=(L+R)*(R-L+1)/2*t;
}
cout<<n*k-ans;
}
例题2:U87406 整除分块
这个题其实比上面的题目还裸,因为没有涉及到任何变形,唯一要记住的就是 ∑ i = 1 n i 2 = n ∗ ( n + 1 ) ∗ ( 2 n + 1 ) 6 \sum_{i=1}^n i ^2=\frac{n*(n+1)*(2n+1)}{6} ∑i=1ni2=6n∗(n+1)∗(2n+1).
#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int mod=1e9+7;
typedef long long ll;
ll inv(ll base,ll x)
{
ll ans=1;
while(x){
if(x&1) ans=ans*base%mod;base=base*base%mod;x>>=1;}
return ans;
}
int main()
{
close;int T;cin>>T;
ll rec6=inv(6,mod-2);
while(T--)
{
int n;cin>>n;
ll last=0,ans=0;
for(ll L=1,R;L<=n;L=R+1)
{
ll t=n/L;R=n/t;
ll cur=R*(R+1)%mod*(2*R+1)%mod*rec6%mod;
ans=(ans+((cur-last)%mod+mod)%mod*t%mod)%mod;
last=cur;
}
cout<<ans<<endl;
}
}
例题3:CF 2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) C题
这道题定义了 F ( n ) = ∑ p ∣ n n F(n)=\sum_{p|n}n F(n)=∑p∣nn,想要求解的是 S = ∑ a ≤ x ≤ b F ( n ) S=\sum_{a\le x\le b}F(n) S=∑a≤x≤bF(n).如果我们仅仅从每个数字的因子个数入手,就必须判断这个数有哪些因子,不可取。而我们转换一下,我们考虑每个数能作为多少个数的因子来进行统计(有点像位运算求和的意思),这样就转换成了除法分块问题。
我们令 S ( n ) = ∑ i = 1 n F ( n ) S(n)=\sum_{i=1}^n F(n) S(n)=∑i=1nF(n),那么就有 S = S ( b ) − S ( a − 1 ) S=S(b)-S(a-1) S=S(b)−S(a−1).然后对于 S ( n ) S(n) S(n)来说, i i i会成为 ⌊ n i ⌋ \big \lfloor \frac{n}{i} \big \rfloor ⌊in⌋个数的因子,因此可以得出: S ( n ) = ∑ i = 1 n i ∗ ⌊ n i ⌋ S(n)=\sum_{i=1}^n i*\big \lfloor \frac{n}{i} \big \rfloor S(n)=i=1∑ni∗⌊in⌋(注意最极限的答案会爆long long)
#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
ull solve(ull x)
{
if(x==1) return 1;
ull ans=0;
for(ull L=1,R;L<=x;L=R+1)
{
ull t=x/L;R=x/t;
ans+=(L+R)*(R-L+1)/2*t;
}
return ans;
}
int main()
{
close;ull a,b;cin>>a>>b;
ull ansa=solve(a-1),ansb=solve(b);
cout<<ansb-ansa;
}
回到原来的D题上,其实这道题经过化简,就是要求解: ∑ p = 1 n ( ⌊ n p ⌋ ) ∗ ( n − p ∗ ⌊ n p ⌋ ) = n ∗ ∑ p = 1 n ⌊ n p ⌋ − ∑ p = 1 n p ∗ ( ⌊ n p ⌋ ) 2 \sum_{p=1}^n \big ( \big \lfloor \frac {n}{p} \big \rfloor \big )* \big ( n-p* \big \lfloor \frac {n}{p} \big \rfloor \big )=n*\sum_{p=1}^n \big \lfloor \frac {n}{p} \big \rfloor \ - \sum_{p=1}^n p* (\big \lfloor \frac {n}{p} \big \rfloor)^2 p=1∑n(⌊pn⌋)∗(n−p∗⌊pn⌋)=n∗p=1∑n⌊pn⌋ −p=1∑np∗(⌊pn⌋)2回到了最初的分块问题上。
const int mod=1e9+7;
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 返回1-n的所有k*m的和
* @param num long长整型 正整数 n
* @return long长整型
*/
long long inv(long long base,long long x)
{
long long ans=1;
while(x){
if(x&1) ans=ans*base%mod;base=base*base%mod;x>>=1;}
return ans;
}
long long cowModCount(long long num) {
long long rec2=inv(2,mod-2);
long long ans=0;
for(long long L=1,R;L<=num;L=R+1)
{
long long t=num/L;R=num/t;
ans=(((ans+num*t%mod*(R-L+1)%mod)%mod-t*t%mod*(L+R)%mod*(R-L+1)%mod*rec2%mod)%mod+mod)%mod;
}
return ans;
}
};