GDOI2018 day2 T1 被吊打后下定决心来学反演。。。
(图片来源于symbol的ppt)
Bzoj2301
https://www.lydsy.com/JudgeOnline/problem.php?id=2301
设 f(n) f ( n ) 表示1~x、1~y中gcd为n的对数(x< y)
f(n)=∑xi=1∑yj=1[gcd(i,j)=n] f ( n ) = ∑ i = 1 x ∑ j = 1 y [ g c d ( i , j ) = n ] ([]是艾佛森括号)
f(n)比较难算,所以再设一个g(n)表示gcd是n倍数的对数
g(n)=∑xi=1∑yj=1[n|gcd(i,j)] g ( n ) = ∑ i = 1 x ∑ j = 1 y [ n | g c d ( i , j ) ] (|是整除)
也可以这样写
g(n)=∑⌊xn⌋d=1f(dn) g ( n ) = ∑ d = 1 ⌊ x n ⌋ f ( d n )
g(n)很容易算,就等于
g(n)=⌊xn⌋⌊yn⌋ g ( n ) = ⌊ x n ⌋ ⌊ y n ⌋
然后用g乱搞就可以得到50分
然而70分怎么搞。。。
好吧直接开始讲正题
假设有一个函数g,其表达式为
g(n)=∑d|nf(d) g ( n ) = ∑ d | n f ( d )
并且g很好求,但f不好求
且最终答案跟f有关
所以要怎么求f?
观察一下g的值
g(1)=f(1) g ( 1 ) = f ( 1 )
g(2)=f(1)+f(2) g ( 2 ) = f ( 1 ) + f ( 2 )
g(3)=f(1)+f(3) g ( 3 ) = f ( 1 ) + f ( 3 )
g(4)=f(1)+f(2)+f(4) g ( 4 ) = f ( 1 ) + f ( 2 ) + f ( 4 )
g(5)=f(1)+f(5) g ( 5 ) = f ( 1 ) + f ( 5 )
g(6)=f(1)+f(2)+f(3)+f(6) g ( 6 ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + f ( 6 )
g(7)=f(1)+f(7) g ( 7 ) = f ( 1 ) + f ( 7 )
g(8)=f(1)+f(2)+f(4)+f(8) g ( 8 ) = f ( 1 ) + f ( 2 ) + f ( 4 ) + f ( 8 )
g(9)=f(1)+f(3)+f(9) g ( 9 ) = f ( 1 ) + f ( 3 ) + f ( 9 )
g(10)=f(1)+f(2)+f(5)+f(10) g ( 10 ) = f ( 1 ) + f ( 2 ) + f ( 5 ) + f ( 10 )
g(11)=f(1)+f(11) g ( 11 ) = f ( 1 ) + f ( 11 )
g(12)=f(1)+f(2)+f(3)+f(4)+f(6)+f(12) g ( 12 ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + f ( 4 ) + f ( 6 ) + f ( 12 )
那么f可以用g求出来
f(1)=g(1) f ( 1 ) = g ( 1 )
f(2)=g(2)−g(1) f ( 2 ) = g ( 2 ) − g ( 1 )
f(3)=g(3)−g(1) f ( 3 ) = g ( 3 ) − g ( 1 )
f(4)=g(4)−g(2) f ( 4 ) = g ( 4 ) − g ( 2 )
f(5)=g(5)−g(1) f ( 5 ) = g ( 5 ) − g ( 1 )
f(6)=g(6)−g(3)−g(2)+g(1) f ( 6 ) = g ( 6 ) − g ( 3 ) − g ( 2 ) + g ( 1 )
f(7)=g(7)−g(1) f ( 7 ) = g ( 7 ) − g ( 1 )
f(8)=g(8)−g(4) f ( 8 ) = g ( 8 ) − g ( 4 )
f(9)=g(9)−g(3) f ( 9 ) = g ( 9 ) − g ( 3 )
f(10)=g(10)−g(5)−g(2)+g(1) f ( 10 ) = g ( 10 ) − g ( 5 ) − g ( 2 ) + g ( 1 )
f(11)=g(11)−g(1) f ( 11 ) = g ( 11 ) − g ( 1 )
f(12)=g(12)−g(6)−g(4)+g(2) f ( 12 ) = g ( 12 ) − g ( 6 ) − g ( 4 ) + g ( 2 )
可以看出f与g之间是有某些规律的
那么可以写成这样
f(n)=∑d|ng(d)∗? f ( n ) = ∑ d | n g ( d ) ∗ ?
可以发现?处是与 nd n d 有关,将其记作 μ(nd) μ ( n d )
μ μ 的取值如下:
μ(n)=⎧⎩⎨1,n=1(−1)kn=p1∗p2∗...∗pk0 μ ( n ) = { 1 , n = 1 ( − 1 ) k n = p 1 ∗ p 2 ∗ . . . ∗ p k 0
其中p为互质的质数
如此便有
g(n)=∑d|nf(d) g ( n ) = ∑ d | n f ( d )
f(n)=∑d|ng(d)∗μ(nd) f ( n ) = ∑ d | n g ( d ) ∗ μ ( n d )
或
g(n)=∑⌊xn⌋d=1f(dn) g ( n ) = ∑ d = 1 ⌊ x n ⌋ f ( d n )
f(n)=∑⌊xn⌋d=1g(dn)∗μ(d) f ( n ) = ∑ d = 1 ⌊ x n ⌋ g ( d n ) ∗ μ ( d )
(其实下面的式子原理跟上面类似,都是感性理解容斥原理)
实际上可以根据它的性质来线性求出。
不会线筛看这里:https://blog.csdn.net/gmh77/article/details/77413368
线筛有一个性质:当它在 i%p[j]=0 i%p[j]=0 时就会退出
所以可以以此来判断出每个数是否含有多个相同质因子
因为 μ μ 是积性函数,所以 μ(i)∗μ(p[j])=−μ(i) μ ( i ) ∗ μ ( p [ j ] ) = − μ ( i )
大概就这样搞,可以线性处理出 μ μ 的值
void init()
{
int i,j;
miu[1]=1;
sum[1]=1;
Len=0;
fo(i,2,len)
{
if (!f[i])
{
p[++Len]=i;
miu[i]=-1;
}
fo(j,1,Len)
if (i*p[j]<=len)
{
f[i*p[j]]=1;
if (!(i%p[j]))//如果含有多个相同质因子
{
miu[i*p[j]]=0;
break;
}
miu[i*p[j]]=-miu[i];//求μ的值
}
else
break;
sum[i]=sum[i-1]+miu[i];//求μ的前缀和,很多题都要用到
}
}
根据上面50分的做法,
g(n)=∑⌊xn⌋d=1f(dn) g ( n ) = ∑ d = 1 ⌊ x n ⌋ f ( d n )
可得
f(n)=∑⌊xn⌋d=1g(dn)∗μ(d) f ( n ) = ∑ d = 1 ⌊ x n ⌋ g ( d n ) ∗ μ ( d )
f(n)=∑⌊xn⌋d=1⌊xdn⌋⌊ydn⌋∗μ(d) f ( n ) = ∑ d = 1 ⌊ x n ⌋ ⌊ x d n ⌋ ⌊ y d n ⌋ ∗ μ ( d )
于是暴力求就有70分了……
然后通过打表观察可以发现
⌊nd⌋ ⌊ n d ⌋ 最多有 n−−√ n 级别种取值
①d< n−−√ n
因为d最多只有 n−−√ n 种取值,所以 ⌊nd⌋ ⌊ n d ⌋ 有 n−−√ n 种取值
②d> n−−√ n
因为 ⌊nd⌋ ⌊ n d ⌋ < n−−√ n ,所以共有 n−−√ n 种取值
于是分别按照 ⌊xdn⌋ ⌊ x d n ⌋ 和 ⌊ydn⌋ ⌊ y d n ⌋ 分成 x−−√+y√ x + y 段,每段的取值都相同,所以可以用 μ μ 的前缀和直接求
时间复杂度: O(x−−√+y√) O ( x + y )
//Bzoj2301 - Proble b
#include
#include
#include
#include
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a
#define len 50000
using namespace std;
int miu[len+1]; //μ
bool f[len+1];
int p[len+1];
int sum[len+1];
int A,B,C,D,K,i,j,k,l,Len,T;
double X,Y;
void swap(int &a,int &b) {int c=a;a=b;b=c;}
void init()
{
int i,j;
miu[1]=1;
sum[1]=1;
Len=0;
fo(i,2,len)
{
if (!f[i])
{
p[++Len]=i;
miu[i]=-1;
}
fo(j,1,Len)
if (i*p[j]<=len)
{
f[i*p[j]]=1;
if (!(i%p[j]))
{
miu[i*p[j]]=0;
break;
}
miu[i*p[j]]=-miu[i];
}
else
break;
sum[i]=sum[i-1]+miu[i];
}
}
int Ans(int x,int y)
{
int i,j,k,ans;
if (x>y) swap(x,y);
k=x/K;
for (ans=0,i=0,j=0; ix /(x/((i+1)*K))/K,y/(y/((i+1)*K))/K);
ans+=(sum[j]-sum[i])*(x/(j*K))*(y/(j*K));
i=j;
}
return ans;
}
int main()
{
init();
for (scanf("%d",&T); T; T--)
{
scanf("%d%d%d%d%d",&A,&B,&C,&D,&K);
printf("%d\n",Ans(B,D)-Ans(A-1,D)-Ans(B,C-1)+Ans(A-1,C-1));
}
return 0;
}
JZOJ5701. 【gdoi2018 day2T1】谈笑风生
http://172.16.0.132/senior/#contest/show/2372/4
因为 min(Ax,Ay)<=1000 m i n ( A x , A y ) <= 1000 (比赛时的题面)
所以可以以小的那边来做,另一边直接分段等差数列求和
(考试时数组开小写炸了)
不难也就想了两个小时
两端的数为n和m,f(d)表示
f(d)=∑ni=1∑mj=1(i+j)[gcd(i,j)=d] f ( d ) = ∑ i = 1 n ∑ j = 1 m ( i + j ) [ g c d ( i , j ) = d ]
f直接求比较蛋疼,考虑设g来辅助
g(d)=∑ni=1∑mj=1(i+j)[d|gcd(i,j)] g ( d ) = ∑ i = 1 n ∑ j = 1 m ( i + j ) [ d | g c d ( i , j ) ]
g(d)=∑⌊nd⌋i=1f(di) g ( d ) = ∑ i = 1 ⌊ n d ⌋ f ( d i )
f(d)=∑⌊nd⌋i=1g(di)∗μ(i) f ( d ) = ∑ i = 1 ⌊ n d ⌋ g ( d i ) ∗ μ ( i )
显然 g可以直接求
g(d)=d⌊nd⌋⌊md⌋(2+⌊nd⌋+⌊md⌋)2 g ( d ) = d ⌊ n d ⌋ ⌊ m d ⌋ ( 2 + ⌊ n d ⌋ + ⌊ m d ⌋ ) 2
(大概就是两个等差数列乱搞)
那么
f(d)=∑⌊nd⌋i=1di⌊ndi⌋⌊mdi⌋(2+⌊ndi⌋+⌊mdi⌋)μ(i)2 f ( d ) = ∑ i = 1 ⌊ n d ⌋ d i ⌊ n d i ⌋ ⌊ m d i ⌋ ( 2 + ⌊ n d i ⌋ + ⌊ m d i ⌋ ) μ ( i ) 2
因为最后求的是互质情况下的和,所以d=1
所以
Ans=∑ni=1i⌊ni⌋⌊mi⌋(2+⌊ni⌋+⌊mi⌋)μ(i)2 A n s = ∑ i = 1 n i ⌊ n i ⌋ ⌊ m i ⌋ ( 2 + ⌊ n i ⌋ + ⌊ m i ⌋ ) μ ( i ) 2
同理,分块就可以 O(n−−√+m−−√) O ( n + m ) 解决一次询问
(注意维护的是 i∗μ(i) i ∗ μ ( i ) 的前缀和)
求边权(只有反演部分)
#include
#include
#include
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a
#define Len 100000
using namespace std;
int miu[Len+1];
bool f[Len+1];
int p[Len+1];
int sum[Len+1];
int i,j,n,m,len;
long long ans,x,y;
void init()
{
int i,j;
miu[1]=1;sum[1]=1;
fo(i,2,Len)
{
if (!f[i])
{
p[++len]=i;
miu[i]=-1;
}
fo(j,1,len)
if (i*p[j]<=Len)
{
f[i*p[j]]=1;
if (!(i%p[j]))
{
miu[i*p[j]]=0;
break;
}
miu[i*p[j]]=-miu[i];
}
else
break;
sum[i]=sum[i-1]+miu[i]*i;
}
}
int main()
{
// freopen("magic.in","r",stdin);
// freopen("magic.out","w",stdout);
init();
scanf("%d%d",&n,&m);
if (n>m) n^=m,m^=n,n^=m;
for (ans=0,i=0,j=0; i1 )),m/(m/(i+1)));
x=n/j;
y=m/j;
ans=ans+(sum[j]-sum[i])*x*y*(2+x+y)/2;
i=j;
}
printf("%lld\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
完整版
#include
#include
#include
#include
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a
#define max(a,b) (a>b?a:b)
#define Len 100000
using namespace std;
long long A[10001];
long long a[40001][2];
long long ls[10001];
long long b[20001];
long long miu[Len+1];
bool f[Len+1];
long long p[Len+1];
long long sum[Len+1];
long long d[1000001];
long long F[10001];
long long i,j,k,n,m,len,len2,h,t;
long long T,s,l,r,mid;
void New(long long x,long long y,long long s)
{
len2++;
a[len2][0]=y;
a[len2][1]=ls[x];
b[len2]=s;
ls[x]=len2;
}
void init()
{
long long i,j;
miu[1]=1;sum[1]=1;
fo(i,2,Len)
{
if (!f[i])
{
p[++len]=i;
miu[i]=-1;
}
fo(j,1,len)
if (i*p[j]<=Len)
{
f[i*p[j]]=1;
if (!(i%p[j]))
{
miu[i*p[j]]=0;
break;
}
miu[i*p[j]]=-miu[i];
}
else
break;
sum[i]=sum[i-1]+miu[i]*i;
}
}
long long get(long long n,long long m)
{
long long ans,x,y;
long long i,j;
if (n>m) n^=m,m^=n,n^=m;
for (ans=0,i=0,j=0; i1 )),m/(m/(i+1)));
x=n/j;
y=m/j;
ans=ans+(sum[j]-sum[i])*x*y*(2+x+y)/2;
i=j;
}
return ans;
}
bool pd(long long s)
{
long long i,S;
memset(F,127,sizeof(F));
h=0;
t=1;
d[1]=1;
F[1]=0;
while (hfor (i=ls[d[++h]]; i; i=a[i][1])
{
S=max(0,b[i]-s);
if (F[d[h]]+S0]])
{
if (a[i][0]0];
F[a[i][0]]=F[d[h]]+S;
}
}
}
return F[n]<=T;
}
int main()
{
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
init();
scanf("%lld%lld%lld",&n,&m,&T);
fo(i,1,n)
scanf("%lld",&A[i]);
fo(i,1,m)
{
scanf("%lld%lld",&j,&k);
s=get(A[j],A[k]);
New(j,k,s);
New(k,j,s);
}
l=0;
r=607931674418546;
while (l2;
if (!pd(mid))
l=mid+1;
else
r=mid;
}
pd(l);
printf("%lld %lld\n",l,F[n]);
fclose(stdin);
fclose(stdout);
return 0;
}
JZOJ4161 于神之怒
http://172.16.0.132/senior/#main/show/4161
同样的套路(x< y)
设f(d)表示
f(d)=∑⌊xd⌋i=1∑⌊yd⌋j=1[gcd(i,j)=1] f ( d ) = ∑ i = 1 ⌊ x d ⌋ ∑ j = 1 ⌊ y d ⌋ [ g c d ( i , j ) = 1 ]
设g(d)表示
g(d)=∑⌊xd⌋i=1∑⌊yd⌋j=11|[gcd(i,j)] g ( d ) = ∑ i = 1 ⌊ x d ⌋ ∑ j = 1 ⌊ y d ⌋ 1 | [ g c d ( i , j ) ]
g(d)=∑⌊xd⌋i=1f(di) g ( d ) = ∑ i = 1 ⌊ x d ⌋ f ( d i )
f(d)=∑⌊xd⌋i=1g(di)μ(i) f ( d ) = ∑ i = 1 ⌊ x d ⌋ g ( d i ) μ ( i )
无比显然地得到
g(d)=⌊xd⌋⌊yd⌋ g ( d ) = ⌊ x d ⌋ ⌊ y d ⌋
f(d)=∑⌊xd⌋i=1⌊xdi⌋⌊ydi⌋μ(i) f ( d ) = ∑ i = 1 ⌊ x d ⌋ ⌊ x d i ⌋ ⌊ y d i ⌋ μ ( i )
那么枚举d,便可得到答案
Ans=∑xd=1dk∑⌊xd⌋i=1⌊xdi⌋⌊ydi⌋μ(i) A n s = ∑ d = 1 x d k ∑ i = 1 ⌊ x d ⌋ ⌊ x d i ⌋ ⌊ y d i ⌋ μ ( i )
Ans=∑xd=1dk∑⌊xd⌋i=1⌊⌊xd⌋⌊yd⌋i⌋μ(i) A n s = ∑ d = 1 x d k ∑ i = 1 ⌊ x d ⌋ ⌊ ⌊ x d ⌋ ⌊ y d ⌋ i ⌋ μ ( i )
把前后都分一下块来搞,最终复杂度: O(x−−√∗x−−√)=O(x) O ( x ∗ x ) = O ( x )
//Jzoj4161 - 于神之怒
#include
#include
#include
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a
#define Len 5000000
#define mod 1000000007
using namespace std;
long long miu[Len+1];
long long sum[Len+1];
long long Sum[Len+1];
bool f[Len+1];
long long p[Len+1];
long long N,M,K,len,i,j,k,l;
long long ans;
void swap(long long &x,long long &y) {long long z=x;x=y;y=z;}
long long qpower(long long a,long long b)
{
long long ans=1;
while (b)
{
if (b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void init()
{
long long i,j;
miu[1]=1;sum[1]=1;Sum[1]=1;
fo(i,2,N)
{
if (!f[i])
{
Sum[i]=qpower(i,K);
p[++len]=i;
miu[i]=-1;
}
long long k=N/i;
fo(j,1,len)
if (p[j]<=k)
{
f[i*p[j]]=1;
Sum[i*p[j]]=(Sum[p[j]]*Sum[i])%mod;
if (!(i%p[j]))
{
miu[i*p[j]]=0;
break;
}
miu[i*p[j]]=-miu[i];
}
else break;
sum[i]=sum[i-1]+miu[i];
}
}
long long Ans(long long d)
{
long long i,j,k,ans;
for (ans=0,i=0,j=0,k=N/d; i1 )))/d,M/(M/(d*(i+1)))/d);
ans=(ans+(sum[j]-sum[i])*(N/(d*j))*(M/(d*j)))%mod;
i=j;
}
return ans;
}
int main()
{
scanf("%lld%lld%lld",&N,&M,&K);
init();
if (N>M) swap(N,M);
fo(i,1,N) Sum[i]=(Sum[i]+Sum[i-1])%mod;
for (ans=0,i=0,j=0; i1)),M/(M/(i+1)));
ans=(ans+(Sum[j]-Sum[i])*Ans(j))%mod;
i=j;
}
printf("%lld\n",(ans+mod)%mod);
}
待补