[CQOI2017]小Q的表格

博客围绕一道算法题展开,先从题目给出的规律\(f(a,b)=f(b,a)\)和\(b\times f(a,a+b)=(a+b)\times f(a,b)\)进行推导,得出修改\(a,b\)值的影响结论,接着通过一系列数学运算化简求解公式,最后为支持修改和查询前缀和,选用分块数据结构优化复杂度。

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

题目

神仙题,神仙题

这是一道很适合盯着发呆的题目

看到这个规律

\[ f(a,b)=f(b,a) \]

\[ b\times f(a,a+b)=(a+b)\times f(a,b) \]

这也没什么规律啊

于是自闭了

盯着发呆一个小时之后发现,这个\(f(a,a+b)\)\(f(a,b)\)有关系

因为修改\((a,b)\)就一定会影响\((a,a+b)\),同时也会影响\((a,a+2b)...\)

\[ gcd(a,a+b)=gcd(a+b,a)=gcd(a,a+b-a)=gcd(a,b) \]

这不是更相减损术吗

于是我们得出了第一个结论

修改\(a,b\)这个值只会影响\(gcd(x,y)=gcd(a,b)\)\(f(x,y)\)的值

但是这样我们还是没有什么办法来维护啊,毕竟矩阵那么大,我们修改一次影响的数那么多

我们大胆猜想格子的值存在某种关系,如果\(gcd(a,b)=d\),那么\(f(a,b)\)肯定和\(f(d,d)\)存在某种关系

尝试去求一下这个关系

\[f(d,d)\times 2d=f(d,2d)\times d\]

\[ f(d,2d)\times 3d=f(d,3d)\times 2d \]

显然\(f(d,kd)=\frac{k}{(k-1)}f(d,(k-1)d)=k\times f(d,kd)\)

显然纵坐标也会有这样的性质

于是\(f(k_1d,k_2d),k_1\perp k_2\),就会有\(k_1\times k_2\times f(d,d)=f(k_1d,k_2d)\)

其实也就是这样

\[f(a,b)=\frac{a\times b}{(a,b)^2}f((a,b),(a,b))\]

考虑把\(f(a,b)\)写成\(\frac{ab}{d^2}f(d,d)\)

于是我们只需要记录\(f(d,d)\)的值了,这样就可以处理修改操作了

接下来把\(f(d,d)\)简记做\(f(d)\)

现在我们要求的柿子是

\[\sum_{i=1}^n\sum_{j=1}^n\frac{i\times j}{(i,j)^2}f((i,j))\]

考虑一下枚举\(gcd\)

\[\sum_{d=1}^nf(d)\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}[(i,j)=1]i\times j\]

那个除以\((i,j)^2\)消失了是因为我们后面乘上的是\(i,j\),本来就是都除以\(d\)了的

之后只要记住一条,千万别反演就好了

我们能通过欧拉函数把上面的柿子写成这个样子

\[ \sum_{d=1}^nf(d)\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}i^2\varphi(i) \]

至于为什么,我们需要这个柿子

\[\sum_{i=1}^n[(i,n)=1]i=\frac{n\varphi(n)+[n=1]}{2}\]

至于这个柿子这么来的,我们证明一下

\[ \sum_{i=1}^n[(i,n)=1]i=\sum_{i=1}^ni\sum_{d|i,d|n}\mu(d) \]

交换一下求和符号

\[ =\sum_{d|n}\mu(d)\sum_{d|i}i=\sum_{d|n}\mu(d)\sum_{i=1}^{\frac{n}{d}}i\times d \]

\[ =\sum_{d|n}\mu(d)d\sum_{i=1}^{\frac{n}{d}}i=\sum_{d|n}\mu(d)d\frac{(\frac{n}{d}+1)\frac{n}{d}}{2} \]

\[ =\frac{n}{2}\sum_{d|n}\mu(d)(\frac{n}{d}+1)=\frac{n}{2}(n\sum_{d|n}\frac{\mu(d)}{d}+\sum_{d|n}\mu(d)) \]

我们现在需要两条很基础的结论

\[\sum_{d|n}\mu(d)=[n=1],\sum_{d|n}\frac{\mu(d)}{d}=\frac{\varphi(n)}{n}\]
这里就不再证明了

根据上面那条结论我们有

\[\sum_{i=1}^n\sum_{j=1}^n[(i,j)=1]i\times j=\sum_{i=1}^ni^2\times \varphi(i)\]

我们设\(S(n)=\sum_{i=1}^ni^2\times \varphi(i)\)

答案就是

\[\sum_{i=1}^nS(\left \lfloor \frac{n}{d} \right \rfloor)f(d)\]

我们现在就可以尽情的整除分块了

但是由于\(f\)需要支持修改我们还要查询前缀和,于是看起来有点自闭,因为树状数组的复杂度高达\(O(m\sqrt{n}logn)\),好像不是很科学

但是修改却快的一批,低到\(O(mlogn)\),考虑一个神奇的数据结构,可以做到\(O(1)\)单点求和

自然是神奇的分块了,我们直接把\(f\)做成前缀和,单点修改我们直接搞成区间修改,之后我们单点查询前缀和就可以很快了

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=4e6+5;
const LL mod=1e9+7;
inline int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
inline LL read() {
    char c=getchar();LL x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int f[maxn],p[maxn>>1];
LL phi[maxn],pre[maxn],F[maxn];
int n,m;
struct Block {
    int sz,tot;
    int l[3005],r[3005],id[maxn];
    LL tag[3005];
    inline void Build() {
        sz=std::sqrt(n);
        for(re int i=1;i<=n;i+=sz) {
            l[++tot]=i,r[tot]=min(i+sz-1,n);
            for(re int j=l[tot];j<=r[tot];j++) id[j]=tot;
        }
    }
    inline LL ask(int x) {return (pre[x]+tag[id[x]])%mod;}
    inline void change(int x,LL val) {
        int j=id[x];
        if(x==l[j]) tag[j]+=val,tag[j]=(tag[j]+mod)%mod;
        else for(re int i=x;i<=r[j];i++) pre[i]=(pre[i]+val+mod)%mod;
        j++;while(j<=tot) tag[j]+=val,tag[j]=(tag[j]+mod)%mod,j++;
    }
}B;
int main() {
    m=read(),n=read();
    phi[1]=1;
    for(re int i=2;i<=n;i++) {
        if(!f[i]) p[++p[0]]=i,phi[i]=i-1;
        for(re int j=1;j<=p[0]&&p[j]*i<=n;j++) {
            f[p[j]*i]=1;
            if(i%p[j]==0) {phi[p[j]*i]=p[j]*phi[i];break;}
            phi[p[j]*i]=phi[p[j]]*phi[i];
        }
    }
    for(re LL i=1;i<=n;i++) phi[i]=phi[i-1]+i*i%mod*phi[i]%mod,phi[i]%=mod;
    for(re LL i=1;i<=n;i++) F[i]=(i*i)%mod;
    for(re int i=1;i<=n;i++) pre[i]=pre[i-1]+F[i],pre[i]%=mod;
    B.Build();
    int a,b,k;LL v;
    while(m--) {
        a=read(),b=read(),v=read(),k=read();
        int t=gcd(a,b);LL ans=0;
        B.change(t,-1ll*F[t]);
        F[t]=v/((LL)(a/t)*(LL)(b/t));
        B.change(t,F[t]);
        for(re int l=1,r;l<=k;l=r+1) {
            r=k/(k/l);
            ans+=phi[k/l]*(B.ask(r)-B.ask(l-1)+mod)%mod,ans%=mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10554083.html

根据引用所述,交错序列是一个仅由0和1构成的序列,其中没有相邻的1(可以有相邻的0)。特征值定义为x^ay^b,其中x和y分别表示0和1出现的次数。长度为n的交错序列可能有多个。问题要求计算所有长度为n的交错序列特征值的和除以m的余数。 根据引用所述,输入文件包含一个行,该行包含三个整数n、a、b和m。其中,1≤n≤10000000,0≤a、b≤45,m<100000000。 为了解决这个问题,可以使用动态规划和矩阵快速幂优化的方法,具体实现可以参考引用提到的相关算法算法的思路是通过计算长度为n的交错序列的特征值,然后将所有特征值求和并对m取余数。 具体步骤如下: 1. 使用动态规划计算长度为n的所有交错序列的特征值,将结果保存在一个矩阵中。 2. 使用矩阵快速幂优化,将动态规划的过程进行优化。 3. 对优化后的结果进行求和,并对m取余数。 4. 输出结果。 参考引用给出的博客中的代码实现,可以帮助你更好地理解和实现该算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*](https://blog.csdn.net/weixin_30892987/article/details/99470493)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值