技巧#
自然溢出#
- 自然溢出不会影响低位数据,所以有的时候你不需要取模,而是一个unsigned.
除法取模#
对于式子
细节#
- 注意数据类型,例如
6*(ll)(1<<30)是要出问题的 - 注意函数在位置的取值,不要忘记初始化
单点的求解#
ll euler_phi(ll n) {
ll m = sqrt(n + 0.5);
ll ans = n;
for (ll i = 2; i <= m; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}cppDivisor#
Given and (), please calculate
分析#
下面所有的除法都是舍去小数的整除.
设
的取值在一个区间中是相同的,因此可以把这个求和公式分块计算.(在分块后,需要获知该段区域内的和,所以需要求前缀和)由于的取值为的级别,因此在知道的值的情况下,每个询问可以这个复杂度中计算出来.
对于,使用筛法,并求出其前缀和.
对于,直接暴力计算.
代码#
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using ll=long long;
const int MAXN=50010;
bool isn_p[MAXN];
vector<int> primes;
int mu[MAXN];
int premu[MAXN];
void init_prime(int len){
isn_p[1]=1;
mu[1]=1;
for(int i=2;i<=len;i++){
if(!isn_p[i]){
primes.push_back(i);
mu[i]=-1;
}
for(int j=0;j<primes.size() && i*primes[j]<=len;j++){
isn_p[i*primes[j]]=1;
mu[i*primes[j]]=mu[i]*-1;
if(i%primes[j]==0){
mu[i*primes[j]]=0;
break;
}
}
}
premu[0]=0;
for(int i=1;i<=len;i++)premu[i]=premu[i-1]+mu[i];
}
ll S[MAXN];
void init_S(int len){
for(int i=1;i<=len;i++){
for(int l=1,r;l<=i;l=r+1){
//remember this line
r=i/(i/l);
S[i]+=(ll)(r-l+1)*(i/l);
}
}
}
int main(){
ios::sync_with_stdio(false);
init_prime(50000);
init_S(50000);
int kase;cin>>kase;
while(kase--){
int n,m;cin>>n>>m;
ll ans=0;
// the minimum one will fastly approach to 0, leading the extra parts of bigger one do nothing to the answer.
int minn=min(n,m);
for(int l=1,r;l<=minn;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=S[n/l]*S[m/l]*(premu[r]-premu[l-1]);
}
cout<<ans<<endl;
}
return 0;
}cpptable#
给出多组,求
分析#
这道题让谁都能看出来重点是如何处理条件
可是这我显然不知道该怎么做.
对询问作以从小到大离线处理,询问前先处理新增的影响.
在已经满足条件的前提下对公式作化简.
可以看到还是套路,引入d,引入,之后胡乱化简.
按照这个式子,需要计算的就是的前缀和.
接下来是如何处理条件…
当每扩大一点,就有一部分被加入到函数的各个部分.使用一种数据结构来维护的前缀和,例如树状数组.最大到,每次受到影响的就是的倍数,以此可以计算出总的时间复杂度为
至此,问题就解决了.
代码#
又丑又长.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cassert>
using namespace std;
using ll=long long;
const int MAXN=100010;
const int MAXQ=100010;
const int MAXFT=400010;
const ll P=(ll)1<<31;
ll FT[MAXFT];
ll lowbit(int x){
return x&-x;
}
void ftadd(int pos,ll x){
for(int i=pos;i<MAXN;i+=lowbit(i)){
FT[i]=(FT[i]+x);
}
}
ll ftget(int pos){
ll res=0;
for(int i=pos;i;i-=lowbit(i)){
res=(res+FT[i]);
}
return res;
}
ll qpow(ll a,ll b,ll p){
ll res=1;
for(b;b;b>>=1,a=(a*a)%p){
if(b&1)res=(res*a)%p;
}
return res;
}
ll qpow(ll a,ll b){
ll res=1;
for(b;b;b>>=1,a=(a*a)){
if(b&1)res=(res*a);
}
return res;
}
bool is_np[MAXN];
ll sigma[MAXN],mu[MAXN];
int t[MAXN];
vector<int> primes;
struct Sig{
ll sigma;
int x;
Sig(){}
Sig(int x,ll sigma):x(x),sigma(sigma){}
};
vector<Sig> sigma_vec;
void init(int n){
is_np[1]=1;
sigma[1]=1;
mu[1]=1;
for(int i=2;i<=n;i++){
if(!is_np[i]){
primes.push_back(i);
sigma[i]=i+1;
t[i]=1;
mu[i]=-1;
}
for(int j=0;j<primes.size() && i*primes[j]<=n;j++){
is_np[i*primes[j]]=1;
sigma[i*primes[j]]=sigma[i]*sigma[primes[j]];
t[i*primes[j]]=1;
mu[i*primes[j]]=mu[i]*-1;
if(i%primes[j]==0){
t[i*primes[j]]=t[i]+1;
sigma[i*primes[j]]=sigma[i/qpow(primes[j],t[i])]*((ll)1-qpow(primes[j],(t[i]+1)+1))/(1-primes[j]);
mu[i*primes[j]]=0;
break;
}
}
}
for(int i=1;i<=n;i++){
sigma_vec.push_back(Sig(i,sigma[i]));
}
}
struct Q{
int n,m,a;
int i;
ll ans;
bool operator<(const Q &b)const{
return a<b.a;
}
} qs[MAXQ];
ll f(int n){
return ftget(n);
}
int curidx=0;
void mergea(int newa){
for(;curidx<sigma_vec.size();curidx++){
Sig &sig=sigma_vec[curidx];
if(sig.sigma>newa)break;
for(int i=sig.x;i<MAXN;i+=sig.x){
ftadd(i,mu[i/sig.x]*sig.sigma%P);
}
}
}
int main(){
init(100000);
sort(sigma_vec.begin(),sigma_vec.end(),[](Sig &a,Sig &b){
return a.sigma<b.sigma;
});
int qlen;cin>>qlen;
for(int i=0;i<qlen;i++){
Q &q=qs[i];
scanf("%d%d%d",&q.n,&q.m,&q.a);
q.i=i;
}
sort(qs,qs+qlen,[](Q &a,Q &b){
return a.a<b.a;
});
for(int i=0;i<qlen;i++){
Q &q=qs[i];
int n=q.n,m=q.m,a=q.a;
ll &ans=q.ans=0;
mergea(a);
for(int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
ans=ans+(n/l)*(m/l)*(f(r)-f(l-1));
}
}
sort(qs,qs+qlen,[](Q &a,Q &b){
return a.i<b.i;
});
for(int i=0;i<qlen;i++){
printf("%lld\n",qs[i].ans%P);
}
return 0;
}cppproduct#
定义斐波纳妾(?)(linux这输入法够魔性)函数
求
分析#
首先,先引个是没错了.
但是
这道题,我又不会.我不知道该怎么处理…蔡就完事了.
现在来看,当引入一个后,在该求积公式里出现了相同项相乘.将该部分的计算调整为幂,剩下的就又都一样了.
设,求前缀和就完事了.
这道题需要使用欧拉定理来优化求幂的速度.
欧拉定理#
当,有以下式子
当,有扩展欧拉定理
根据这2个式子,可以在快速降幂,来加快运算.
代码#
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
using ll=long long;
const int MAXN=1000010;
const ll P=1e9+7;
inline ll qpow(ll a,ll b,ll p){
ll res=1;
for(;b;b>>=1,a=a*a%p){
if(b&1)res=res*a%p;
}
return res;
}
inline ll get_inv(ll a,ll p){
return qpow(a,p-2,p);
}
bool is_np[MAXN];
vector<int> primes;
ll f[MAXN], g[MAXN];
ll inv_f[MAXN];
ll preg[MAXN],inv_pg[MAXN];
ll mu[MAXN];
void init(int n){
is_np[1]=1;
mu[1]=1;
for(int i=2;i<=n;i++){
if(!is_np[i]){
primes.push_back(i);
mu[i]=-1;
}
for(int j=0;j<primes.size() && i*primes[j]<=n;j++){
is_np[i*primes[j]]=1;
mu[i*primes[j]]=mu[i]*-1;
if(i%primes[j]==0){
mu[i*primes[j]]=0;
break;
}
}
}
f[1]=f[2]=1;
for(int i=3;i<=n;i++){
f[i]=(f[i-1]+f[i-2])%P;
}
for(int i=1;i<=n;i++){
inv_f[i]=get_inv(f[i],P);
}
for(int i=1;i<=n;i++)g[i]=1;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i){
if(mu[j/i]==-1)g[j]=(g[j]*inv_f[i])%P;
else if(mu[j/i]==1)g[j]=(g[j]*f[i])%P;
//when mu==0,nothing happens
}
}
preg[0]=1;
for(int i=1;i<=n;i++)preg[i]=preg[i-1]*g[i]%P;
inv_pg[0]=1;
for(int i=1;i<=n;i++)inv_pg[i]=get_inv(preg[i],P)%P;
}
int main(){
init(1000000);
//cout<<"done"<<endl;
/*
for(auto i:primes){
cout<<i<<" ";
}
cout<<endl;
for(int i=1;i<=20;i++){
cout<<g[i]<<" ";
}
cout<<endl;
*/
int kase;
scanf("%d",&kase);
while(kase--){
ll n,m;
scanf("%d%d",&n,&m);
if(n>m)swap(n,m);
ll ans=1;
for(ll l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ll sum=preg[r]*inv_pg[l-1]%P;
ans=ans*qpow(sum,(n/l)*(m/l)%(P-1),P)%P;
}
printf("%lld\n",ans);
}
return 0;
}cppphi3#
给出,求
分析#
这道题重点在于
这堆东西的化简.很显然,我又不会.
观察的公式
可以得出上面那一堆等于.剩下的就又都一样了.
设,直接筛.最后计算前缀和的时候再乘上就可以了.
这道题的取模还有这种处理方法: 直接取数组为
unsigned并自然溢出.二进制后30位不会受到影响.
代码#
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
using ull=unsigned long long;
using ll=long long;
const int MAXN=10000010;
const unsigned P=1<<30;
bool isnp[MAXN];
vector<int> primes;
int t[MAXN],M[MAXN];
unsigned Mt[MAXN];
unsigned g[MAXN];
ll preg[MAXN];
void init(int n){
isnp[1]=1;
g[1]=1;
for(int i=2;i<=n;i++){
if(!isnp[i]){
primes.push_back(i);
g[i]=i-1-1;
t[i]=1;
M[i]=Mt[i]=i;
}
for(int j=0;j<primes.size() && i*primes[j]<=n;j++){
int newone=i*primes[j];
isnp[newone]=1;
g[newone]=g[i]*g[primes[j]]%P;
t[newone]=1;
M[newone]=Mt[newone]=primes[j];
if(i%primes[j]==0){
t[newone]=t[i]+1;
Mt[newone]=Mt[i]*primes[j]%P;
g[newone]=g[i/Mt[i]]*(Mt[newone]+Mt[i]/primes[j]-2*Mt[i])%P;
break;
}
}
}
for(int i=1;i<=n;i++){
preg[i]=preg[i-1]+(ll)i*i%P*i%P*g[i]%P;
preg[i]%=P;
}
}
int main(){
init(10000000);
int kase;
scanf("%d",&kase);
while(kase--){
int n;
scanf("%d",&n);
ll ans=0;
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
ull lim=n/l;
ull sum1=lim*(lim+1)%(2*P)/2;
ull sum2=lim*(lim+1)%(6*P)*(2*lim+1)%(6*P)/6;
ans=ans+lim*sum1%P*sum2%P*(preg[r]-preg[l-1])%P;
}
printf("%lld\n",ans%P);
}
return 0;
}cpp