51nod1220约数之和

本文详细解析了51nod1220题目——约数之和的解题过程,利用莫比乌斯反演和杜教筛等技巧,通过数学推导简化了问题并给出了高效的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

51nod1220约数之和

Time Limit 3s Memory Limit 128KB
Description
σ(x)表示x的约束和
ni=1nj=1σ(ij)
Input
一个数n(0<n109)
Output
一个数答,对109+7取模。
Sample Input

1000

Sample Output

576513341

解题思路

莫比乌斯反演,杜教筛的各种技能
定义[x]的取值为1(x为真)否则为0

Ans=i=1nj=1nd|ijd

d|ij能不能化为一个d|j呢,想到d|ij等同于dgcd(i,d)|j

d|ij要满足xy=d[x|i][y|j],x取gcd(i,d)能供给完[x|i]也就是说,这时gcd(y,i)=1当且仅当y|jd|ij成立,即d|ijdgcd(i,d)|j

Ans=i=1nj=1nd=1ijd[dgcd(i,d)|j]=i=1nd=1indngcd(i,d)d=d=1ni=1nd=1in[gcd(i,d)=d]dndd      (dgcd(i,d))

gcd(i,d)=d?
考虑化为gcd(,)=1很简单啊,用idd代替i,用ddd代替d

Ans=d=1nid=1nddd=1idn[gcd(id,dd)=1]dddddn

用i代替id,用d代替dd
Ans=d=1ni=1ndd=1in[gcd(i,d)=1]ddnd=d=1ndi=1ndd=1n[gcd(i,d)=1]dnd=i=1nd=1nidd=1n[gcd(i,d)=1]dnd=i=1n12ni(ni+1)d=1n[gcd(i,d)=1]dnd

gcd(i,d)=1怎么处理呢,想到d|nμ(d)=[n=1]那么
[gcd(i,d)=1]=k|gcd(i,d)μ(k)=k[k|i][k|d]μ(k)


Ans=i=1n12ni(ni+1)d=1ndndd=1n[d|i][d|d]μ(d)=d=1nμ(d)id=1ndiddnidddd=1nd12nddd(nddd+1)=d=1ndμ(d)i=1ndinidd=1nd12ndd(ndd+1)


f(x)=xμ(x)

g(x)=i=1xixi

h(x)=i=1x12xi(xi+1)

其实h和g是等价的

i=1x12xi(xi+1)=i=1xj=1xij(ij)=i=1xj=1xii=i=1xxii


Ans=d=1nf(d)g(nd)h(nd)=d=1ng(nd)2f(d)

我们想,nd只有2n

i<=n时,i[1,n]
i>n时,ni<n
ni只有2n
对于i,有j=max{x|nx=ni}=nni

所以,分块处理
ij[nni1+1,nni]nj=i
只需维护s(x)=xi=1f(x)即可,g(ni)直接算
O(n)=niO(ni)=O(n34)

对于S(x)
d|nμ(d)=[n=1]
那么

1=i=1nid|iμ(d)=d=1ni=1niidμ(d)=d=1ni=1nidiμ(i)=d=1ndi=1nif(i)=d=1nds(nd)

s(n)=1d=2nds(nd)(d=1)

同样,分块处理n4=n22,所以直接搜会有很多重复的项,哈希判重,O(n)=niO(ni)=o(n34)
预处理前n23项,复杂度可降到O(n23)

总复杂度O(n34)

#include<cstring>
#include<cstdio>
#include<cctype>
#define N 3001001
#define lim 43007
#define mo 1000000007
#define ny 500000004
using namespace std;
typedef long long ll;
ll n,s[N],sum[lim],hash[lim],mu[N],pr[N];
bool bz[N];
int get(ll x){
    int y=(int)(x%lim);
    while(hash[y] && hash[y]!=x)y++,y=y==lim?0:y;return y;
}
ll S(ll a){
    if(a<N)return s[a];
    int x=get(a);
    if(hash[x])return sum[x];
    hash[x]=a;ll ans=1;
    for(ll i=2,j;i<=a;i=j+1){
        j=a/(a/i);
        ans=(ans-(j+i)%mo*((j-i+1)%mo)%mo*ny%mo*S(a/i)%mo+mo)%mo;
    }sum[x]=ans;return ans;
}
ll g(ll a){
    ll ans=0;
    for(ll i=1,j;i<=a;i=j+1){
        j=a/(a/i);
        ans=(ans+a/i%mo*((i+j)%mo*((j-i+1)%mo)%mo*ny%mo))%mo;
    }return ans;
}
int main(){
    s[1]=mu[1]=1;
    for(ll i=2;i<N;i++){
        if(!bz[i])mu[pr[++pr[0]]=i]=-1;
        s[i]=(s[i-1]+mu[i]*i%mo+mo)%mo;
        for(int j=1;j<=pr[0] && i*pr[j]<N;j++){
            bz[i*pr[j]]=1;mu[i*pr[j]]=-mu[i];
            if(i%pr[j]==0){mu[i*pr[j]]=0;break;}
        }
    }
    scanf("%lld",&n);
    ll ans=0;
    for(ll i=1,j,las=0,now,t;i<=n;i=j+1,las=now){
        j=n/(n/i);now=S(j);t=g(n/i);
        ans=(ans+(now-las+mo)%mo*t%mo*t%mo)%mo;
    }
    printf("%lld",ans);
}
题目 51nod 3478 涉及一个矩阵问题,要求通过最少的操作次数,使得矩阵中至少有 `RowCount` 行和 `ColumnCount` 列是回文的。解决这个问题的关键在于如何高效地枚举所有可能的行和列组合,并计算每种组合所需的操作次数。 ### 解法思路 1. **预处理每一行和每一列变为回文所需的最少操作次数**: - 对于每一行,计算将其变为回文所需的最少操作次数。这可以通过比较每对对称位置的值是否相同来完成。 - 对于每一列,计算将其变为回文所需的最少操作次数,方法同上。 2. **枚举所有可能的行和列组合**: - 由于 `N` 和 `M` 的最大值为 8,因此可以枚举所有可能的行组合和列组合。 - 对于每一种组合,计算其所需的最少操作次数,并取最小值。 3. **计算操作次数**: - 对于每一种组合,需要计算哪些行和列需要修改,并且注意行和列的交叉点可能会重复计算,因此需要去重。 ### 代码实现 以下是一个可能的实现方式,使用了枚举和位运算来处理组合问题: ```python def min_operations_to_palindrome(matrix, row_count, col_count): import itertools N = len(matrix) M = len(matrix[0]) # Precompute the cost to make each row a palindrome row_cost = [] for i in range(N): cost = 0 for j in range(M // 2): if matrix[i][j] != matrix[i][M - 1 - j]: cost += 1 row_cost.append(cost) # Precompute the cost to make each column a palindrome col_cost = [] for j in range(M): cost = 0 for i in range(N // 2): if matrix[i][j] != matrix[N - 1 - i][j]: cost += 1 col_cost.append(cost) min_total_cost = float('inf') # Enumerate all combinations of rows and columns rows = list(range(N)) cols = list(range(M)) from itertools import combinations for row_comb in combinations(rows, row_count): for col_comb in combinations(cols, col_count): # Calculate the cost for this combination cost = 0 # Add row costs for r in row_comb: cost += row_cost[r] # Add column costs for c in col_comb: cost += col_cost[c] # Subtract the overlapping cells for r in row_comb: for c in col_comb: # Check if this cell is part of the palindrome calculation if r < N // 2 and c < M // 2: if matrix[r][c] != matrix[r][M - 1 - c] and matrix[N - 1 - r][c] != matrix[N - 1 - r][M - 1 - c]: cost -= 1 min_total_cost = min(min_total_cost, cost) return min_total_cost # Example usage matrix = [ [0, 1, 0], [1, 0, 1], [0, 1, 0] ] row_count = 2 col_count = 2 result = min_operations_to_palindrome(matrix, row_count, col_count) print(result) ``` ### 代码说明 - **预处理成本**:首先计算每一行和每一列变为回文所需的最少操作次数。 - **枚举组合**:使用 `itertools.combinations` 枚举所有可能的行和列组合。 - **计算成本**:对于每一种组合,计算其成本,并考虑行和列交叉点的重复计算问题。 ### 复杂度分析 - **间复杂度**:由于 `N` 和 `M` 的最大值为 8,因此枚举所有组合的间复杂度为 $ O(N^{RowCount} \times M^{ColCount}) $,这在实际中是可接受的。 - **空间复杂度**:主要是存储预处理的成本,空间复杂度为 $ O(N + M) $。 ### 相关问题 1. 如何优化矩阵中行和列的枚举组合以减少计算间? 2. 在计算行和列的交叉点,如何更高效地处理重复计算的问题? 3. 如果矩阵的大小增加到更大的范围,如何调整算法以保持效率? 4. 如何处理矩阵中行和列的回文条件不同的情况? 5. 如何扩展算法以支持更多的操作类型,例如翻转某个区域的值?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值