[NOIP模拟2015.10.24]tty的求助III

题目大意

有一个函数 f(x) ,给定函数在 1xmin(n,m)(x>0) 内的值。
试求出

i=1nj=1mf(gcd(i,j))

其中 1n,m105,1xmin(n,m),1f(x)5×108

以为这样就完了吗?
不!

T 次修改操作,每次修改形如(l,r,u)。表示将 f(i)(lir) 的值都乘上 u
每次修改后,要回答上述式子新的值,每次修改影响会持续。
其中1u5×108,1lrmin(n,m)
最终答案对 998244353 取模。


题目分析

我们设

gk=i=1nj=1m|gcd(i,j)=k|

然后最初答案就为:
i=1min(n,m)f(i)gi

然后考虑修改操作,我们发现其实它对答案的影响只是乘上了
i=lrugi

由乘幂运算法则,则等于乘上
uri=lgi

然而我们维护 s g的前缀和即可,利用以上式子算即可。

是不是觉得能过了?是不是有些小激动?
别忘了 gk 怎么求,还没有分析。

如果 n=m ,那这个可以很容易在 O(n) 的时间复杂度内求出。(枚举质因数,用欧拉函数搞一下即可)
但是出题人很恶心, n ,m不一定相等。
我们考虑用容斥原理来解决,显然

gi=ni×mij=2min(n,m)igi×j

即在 [1..n] 中选 i 的倍数,与从[1..m]中选 i 的倍数组合,减去gcd i 的倍数的个数。
那我们将i从大到小枚举计算即可。

时间复杂度分析:

i=1nni=ni=1n1n=n(ln(n+1)+γ)

其中 γ 为欧拉常数。


代码实现

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int N=100000;
    const int P=998244353;
    long long f[N+1],g[N+1];
    int ans,n,m,t;
    int readint()
    {
        int x=0,f=1;
        char ch=getchar();
        while (ch<'0'||ch>'9')
        {
            if (ch=='-')
                f=-1;
            ch=getchar();
        }
        while (ch>='0'&&ch<='9')
        {
            x=x*10+ch-'0';
            ch=getchar();
        }
        return x*f;
    }
    long long readlong()
    {
        long long x=0,f=1;
        char ch=getchar();
        while (ch<'0'||ch>'9')
        {
            if (ch=='-')
                f=-1;
            ch=getchar();
        }
        while (ch>='0'&&ch<='9')
        {
            x=x*10+ch-'0';
            ch=getchar();
        }
        return x*f;
    }
    int quick_power(long long x,long long y)
    {
        if (y==1)
            return x%P;
        int ret=quick_power(x,y>>1);
        ret=(long long)ret*ret%P;
        if (y&1)
            ret=(long long)ret*x%P;
        return ret;
    }
    int main()
    {
        freopen("help3.in","r",stdin);
        freopen("help3.out","w",stdout);
        n=readint(),m=readint();
        if (n>m)
            n^=m^=n^=m;
        for (int i=1;i<=n;i++)
            f[i]=readlong();
        for (int i=n;i>=1;i--)
        {
            g[i]=((long long)n/i)*((long long)m/i);
            for (int j=2;j<=n/i;j++)
                g[i]-=g[i*j];
        }
        ans=1;
        for (int i=1;i<=n;i++)
        {
            ans=(long long)ans*quick_power(f[i],g[i])%P;
            g[i]+=g[i-1];
        }
        printf("%d\n",ans);
        t=readint();
        while (t--)
        {
            int l=readint(),r=readint();
            long long u=readlong();
            ans=(long long)ans*quick_power(u,g[r]-g[l-1])%P;
            printf("%d\n",ans);
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值