上午摸鱼 听课,下午打比赛。
又遇到了一点点小小的账号问题。
[恶意魔印]
题意: Q Q Q 组询问,给定 n , m , a n,m,a n,m,a,对于对于所有满足 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n, 1 ≤ j ≤ m 1 \le j \le m 1≤j≤m, 1 ≤ g c d ( i , j ) ≤ a 1 \le gcd(i,j) \le a 1≤gcd(i,j)≤a 的有序数对 ( i , j ) (i,j) (i,j),求出 l c m ( i , j ) lcm(i,j) lcm(i,j) 之和,对 1 0 9 + 7 10^9+7 109+7 取模。
对于 15 % 15\% 15% 的数据, Q = 1 Q=1 Q=1, n , m , a ≤ 5 × 1 0 3 n,m,a \le 5 \times 10^3 n,m,a≤5×103。
对于 50 % 50\% 50% 的数据, Q = 1 Q=1 Q=1, n , m , a ≤ 5 × 1 0 5 n,m,a \le 5 \times 10^5 n,m,a≤5×105。
对于 100 % 100\% 100% 的数据, Q ≤ 1 0 4 Q \le 10^4 Q≤104, n , m , a ≤ 5 × 1 0 5 n,m,a \le 5 \times 10^5 n,m,a≤5×105。
[考场]
一眼就知道一定和数学有关,不是推公式就是反演。
然后就不会了,想了半个小时暴力草草走人。
[正解]
你是数学FW?我也是!
所以看题解推式子花了我一个小时。
一些前置知识
莫比乌斯函数:
μ
(
d
)
=
{
1
(
d
=
1
)
(
−
1
)
k
(
d
=
p
1
p
2
p
3
.
.
.
p
k
,
p
i
为互异素数
)
0
(
其它情况
)
\mu(d)=\begin{cases} 1(d=1) \\ (-1)^k (d=p_1p_2p_3...p_k,p_i为互异素数)\\0 (其它情况) \end{cases}
μ(d)=⎩
⎨
⎧1(d=1)(−1)k(d=p1p2p3...pk,pi为互异素数)0(其它情况)
狄利克雷卷积:
(
f
∗
g
)
(
n
)
=
∑
d
∣
n
f
(
d
)
g
(
n
d
)
(f*g)(n)=\sum_{d|n}f(d)g(\frac{n}{d})
(f∗g)(n)=∑d∣nf(d)g(dn)
常见推论:
- d ( n ) = ∑ d ∣ n 1 d(n)=\sum_{d|n}1 d(n)=∑d∣n1 即 d = 1 ∗ 1 d=1*1 d=1∗1
- σ ( n ) = ∑ d ∣ n d \sigma(n)=\sum_{d|n}d σ(n)=∑d∣nd 即 σ = I d ∗ 1 \sigma =Id*1 σ=Id∗1
- φ ( n ) = ∑ d ∣ n μ ( d ) n d \varphi(n)=\sum_{d|n}\mu(d)\frac{n}{d} φ(n)=∑d∣nμ(d)dn 即 φ = μ ∗ I d \varphi=\mu*Id φ=μ∗Id
- ε ( n ) = ∑ d ∣ n μ ( d ) \varepsilon(n)=\sum_{d|n}\mu(d) ε(n)=∑d∣nμ(d) 即 ε = μ ∗ 1 \varepsilon=\mu*1 ε=μ∗1
由(3),我们有 ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] ∑d∣nμ(d)=[n=1]
推式子
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
≤
a
]
⋅
l
c
m
(
i
,
j
)
\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)\le a]\cdot lcm(i,j)
∑i=1n∑j=1m[gcd(i,j)≤a]⋅lcm(i,j)
看起来
[
]
[]
[] 内的东西不太好处理,不如再加一层循环?
∑
d
=
1
a
1
d
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
⋅
i
j
\sum_{d=1}^{a}\frac{1}{d}\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=d] \cdot ij
∑d=1ad1∑i=1n∑j=1m[gcd(i,j)=d]⋅ij
把
d
d
d 提出来?
∑
d
=
1
a
d
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
g
c
d
(
i
,
j
)
=
1
]
⋅
i
j
\sum_{d=1}^{a}d\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j)=1] \cdot ij
∑d=1ad∑i=1⌊dn⌋∑j=1⌊dm⌋[gcd(i,j)=1]⋅ij
由上面加粗的公式,我们把
[
]
[]
[] 扔掉
∑
d
=
1
a
d
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
i
j
∑
p
∣
g
c
d
(
i
,
j
)
μ
(
p
)
\sum_{d=1}^{a}d\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor} ij \sum_{p|gcd(i,j)}\mu(p)
∑d=1ad∑i=1⌊dn⌋∑j=1⌊dm⌋ij∑p∣gcd(i,j)μ(p)
把中间两个循环展开,用求和公式代替(
S
(
n
)
=
n
(
n
+
1
)
2
S(n)=\frac{n(n+1)}{2}
S(n)=2n(n+1))
∑
d
=
1
a
d
∑
p
μ
(
p
)
⋅
p
2
⋅
S
(
⌊
n
p
d
⌋
)
⋅
S
(
⌊
m
p
d
⌋
)
\sum_{d=1}^{a}d\sum_{p}\mu(p)\cdot p^2\cdot S(\lfloor \frac{n}{pd}\rfloor)\cdot S(\lfloor \frac{m}{pd}\rfloor)
∑d=1ad∑pμ(p)⋅p2⋅S(⌊pdn⌋)⋅S(⌊pdm⌋)
令
T
=
p
d
T=pd
T=pd,继续化简
∑
T
=
1
S
(
⌊
n
T
⌋
)
⋅
S
(
⌊
m
T
⌋
)
⋅
∑
1
≤
d
≤
a
,
d
∣
T
T
2
d
μ
(
T
d
)
\sum_{T=1} S(\lfloor \frac{n}{T}\rfloor)\cdot S(\lfloor \frac{m}{T}\rfloor)\cdot \sum_{1 \le d \le a,d|T}\frac{T^2}{d}\mu(\frac{T}{d})
∑T=1S(⌊Tn⌋)⋅S(⌊Tm⌋)⋅∑1≤d≤a,d∣TdT2μ(dT)
如何维护
将询问离线,
a
a
a 从小到大排序处理。
对于
T
2
d
μ
(
T
d
)
\frac{T^2}{d}\mu(\frac{T}{d})
dT2μ(dT)用树状数组维护,对于每一个询问,整除分块去做(莫比乌斯反演老伙伴了)。
注意时时刻刻取模。(感谢 CQR DALAO)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=1e5,mod=1e9+7;
typedef long long LL;
struct node
{
int n,m,a,id;
}q[N];
int cmpA(node x,node y){return x.a<y.a;}
inline LL S(int x){return (1LL*x*(x+1)/2LL)%mod;}
inline LL P(LL x){return ((x<0)?(((x%mod)+mod)%mod):(x%mod));}
inline void gmod(LL &x,LL y){x=(x+y)%mod;}
LL res[N],tr[N];
inline int lowbit(int x){return x&(-x);}
inline void modify(int x,LL v){for(;x<=M;x+=lowbit(x))gmod(tr[x],v);}
inline LL query(int x)
{
LL res=0;
for(;x;x-=lowbit(x))gmod(res,tr[x]);
return res;
}
int primes[N],st[N],cnt;
LL mu[N];
void init()
{
mu[1]=1LL;
for(int i=2;i<=M;i++)
{
if(!st[i])
{
primes[cnt++]=i;
mu[i]=-1;
}
for(int j=0;primes[j]*i<=M;j++)
{
st[primes[j]*i]=1;
if(i%primes[j]==0)break;
mu[primes[j]*i]=-mu[i];
}
}
for(int i=1;i<=M;i++)mu[i]=P(1LL*mu[i]*(LL)i%mod*(LL)i%mod);
}
void work(int id)
{
int la=q[id-1].a;
int nn=q[id].n,nm=q[id].m,na=q[id].a;
for(int i=la+1;i<=na;i++)
{
for(int T=i;T<=M;T+=i)modify(T,1LL*i*mu[T/i]%mod);
}
LL ans=0;
for(int l=1,r;l<=nn;l=r+1)
{
r=min(nn/(nn/l),nm/(nm/l));
gmod(ans,1LL*S(nn/l)*S(nm/l)%mod*P(query(r)-query(l-1))%mod);
ans=P(ans);
}
res[q[id].id]=ans;
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
int T;
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a);
if(q[i].n>q[i].m)swap(q[i].n,q[i].m);
q[i].id=i;
}
sort(q+1,q+T+1,cmpA);
init();
for(int i=1;i<=T;i++)work(i);
for(int i=1;i<=T;i++)printf("%lld\n",res[i]);
return 0;
}
[松鼠串门]
题意:给定一棵根为1的树,有 n n n 个节点,每个节点都有权值 a i a_i ai,有 m m m 操作,一个操作是在某节点 u u u 下面挂一个儿子,权值为 v v v ,还有一个操作是从某节点 u u u 开始向根走 d d d 步(超过根到根就停止),路上任意结点的异或和最大是多少。
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 5 × 1 0 5 1 \le n \le 5\times 10^5 1≤n≤5×105, 1 ≤ m ≤ 5 × 1 0 5 1 \le m \le 5\times10^5 1≤m≤5×105, 0 ≤ a i < 2 64 0 \le a_i <2^{64} 0≤ai<264
[考场]
异或和最大?线性基!
然后……聪明 纸张的我直接暴力将路径上的点加入线性基。
[正解]
感谢CYC&CYE DALAO
在这题之前,您可能需要了解这题,我们需要借鉴一些东西。
首先,我们发现插入操作完全对答案是没有影响的,不妨直接先插入后处理询问。
设插入完后有
n
n
n 个点,某个点坐标为
i
i
i,父亲为
p
a
r
i
par_i
pari。
我们建立
n
n
n 个线性基,对于第
i
i
i 个线性基,直接继承
p
a
r
i
par_i
pari 的线性基。
至于插入操作,我们记录深度,如果当前位置不空,比较深度,将深度大的留下,深度小的继续下传。
查询时,注意取出的深度不能超过题目要求的
d
d
d 步就可以了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=1e6+5;
#define x first
#define y second
int n,m;
vector<pair<int,int> >q;
int d[N],par[N];
ULL w[N];
struct node
{
ULL a[70];
int d[70];
}H[N];
void insert(ULL a[],int d[],ULL x,int depth)
{
for(int i=63;~i;i--)
{
if(x>>i&1)
{
if(!a[i])
{
a[i]=x;
d[i]=depth;
return;
}
else
{
if(depth>d[i])
{
swap(a[i],x);
swap(d[i],depth);
}
x^=a[i];
}
}
}
}
ULL query(ULL a[],int d[],int depth)
{
ULL res=0;
for(int i=63;~i;i--)
{
if(depth<=d[i]&&(res^a[i])>res)res^=a[i];
}
return res;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d%llu",&par[i],&w[i]);
while(m--)
{
int op,u;
scanf("%d%d",&op,&u);
if(!op)
{
int d;
scanf("%d",&d);
q.push_back({u,d});
}
else
{
ULL v;
scanf("%llu",&v);
par[++n]=u;
w[n]=v;
}
}
for(int i=1;i<=n;i++)
{
d[i]=d[par[i]]+1;
memcpy(H[i].a,H[par[i]].a,sizeof H[i].a);
memcpy(H[i].d,H[par[i]].d,sizeof H[i].d);
insert(H[i].a,H[i].d,w[i],d[i]);
}
for(auto &[u,t]:q)printf("%llu\n",query(H[u].a,H[u].d,d[u]-t));
return 0;
}
[寻找平衡]
OvO认为一些事情需要花费相同的时间去完成。
QwQ则觉得只需要让一些事情不比另一些事情完成的时间多就够了。
有 n n n 件事情,OvO 有 m 1 m_1 m1 个要求,QwQ 有 m 2 m_2 m2 个要求。
如果原来有一件事情花费了 w w w 的时间,现在若是要花费 x x x 时间去完成它,那就得付出 ∣ w − x ∣ |w-x| ∣w−x∣ 这么多的精力。最少需要花费多少的精力,才能同时满足两人的要求。
对于 100 % 100\% 100% 的数据, n , m 1 ≤ 500 n,m_1 \le 500 n,m1≤500, m 2 ≤ 1 0 5 m_2 \le 10^5 m2≤105,时间 ≤ 1 0 9 \le10^9 ≤109
考场
把OvO的要求用并查集合并,QwQ的要求建图后缩点,最后拓扑排序DP。
然而写挂了。