• 「解题报告」 [UOJ#62] 怎样跑得更快 (莫比乌斯反演)


    「解题报告」 [UOJ#62] 怎样跑得更快 (莫比乌斯反演)

    题意

    给定 (n,c,d), 有 (q) 个询问, 每个询问给定 (b_1,cdots, b_n), 满足

    [sum_{j=1}^ngcd(i,j)^ccdot{ m lcm}(i,j)^dcdot x_j equiv b_i pmod p ]

    对每个询问, 解出 (x_1,cdots,x_n) 的值.


    思路

    目标 : 把原式转化为 (f(x)=sum_{d|x} g(d)) 的形式, 然后用莫比乌斯反演求出 (g(x)).

    下面的运算都是在 (mod p) 意义下的运算.

    [egin{align} b_i &= sum_{j=1}^ngcd(i,j)^ccdot{ m lcm}(i,j)^dcdot x_j \ &= sum_{j=1}^n gcd(i,j)^{c-d} cdot (ij)^d cdot x_j \ &= sum_{u|i} u^{c-d} sum_{u|j}^n [gcd(i,j)=u] cdot (ij)^d cdot x_j \ &= sum_{u|i} x^{c-d} sum_{u|j}^n [frac{gcd(i,j)}{u}=1] cdot (ij)^d cdot x_j \ &= sum_{u|i} u^{c-d} sum_{u|j}^n (ij)^d cdot x_j sum_{v|frac{gcd(i,j)}{u}} mu(v)\ &= sum_{u|i} u^{c-d} sum_{u|j}^n (ij)^d cdot x_j sum_{uv|gcd(i,j)} mu(v)\ end{align} ]

    (T=uv) (最关键的一步)

    [egin{align} b_i &= sum_{u|i} u^{c-d} sum_{u|j}^n (ij)^d cdot x_j sum_{uv|gcd(i,j)} mu(v) \ &= sum_{T|i}sum_{T|j}^n (ij)^dx_j sum_{u|T} u^{c-d}muleft(frac{T}{u} ight) end{align} ]

    其中 (sum_{u|T} u^{c-d}muleft(frac{T}{u} ight)) 是两个积性函数的狄利克雷卷积, 可以线性筛出来, 设为 (h(T)), 则

    [egin{align} b_i &= sum_{T|i}sum_{T|j}^n (ij)^dx_j sum_{u|T} u^{c-d}muleft(frac{T}{u} ight) \ &= i^d sum_{T|i}sum_{T|j}^n j^d x_j h(T) end{align} ]

    (sum_{T|j}^n j^d x_j h(T))(g(T)), (frac{b_i}{i^d})(f(i)), 则

    [egin{align} f(i)=sum_{T|i} g(T) \ end{align} ]

    利用莫比乌斯反演便可 (O(nsqrt n)) 求出 (g(x)). 而

    [egin{align} g(T)&=sum_{T|j}^n j^d x_j h(T) \ &Downarrow \ frac{g(T)}{h(T)} &=sum_{T|j}^n j^d x_j \ &Downarrow \ x_T&=frac{1}{T_d}left( sum_{T|j}^n muleft(frac{j}{T} ight) frac{g(j)}{h(j)} ight) end{align} ]

    便可在 (O(nlog n)) 内求出 (x_n).

    总复杂度为 (O(n+q(n+nsqrt n+nlog n))).

    需要注意优化一下常数, 比如预处理 (x^d) 以及 (x^{c-d}).

    代码

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    const int _=1e5+7;
    const ll mod=998244353;
    int n,T,pri[_],p[_];
    ll C,D,mu[_],pw[2][_],f[_],g[_],h[_],a[_],b[_];
    bool v[_],flag;
    ll gi(){
      ll x=0,f=1; char c=getchar();
      while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
      while(isdigit(c)){ x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
      return x*f;
    }
    ll _pw(ll a,ll p){
      if(p<0) return _pw(_pw(a,-p),mod-2);
      ll res=1;
      while(p){
        if(p&1) res=res*a%mod;
        a=a*a%mod; p>>=1;
      }
      return res;
    }
    ll _inv(ll x){ return _pw(x,mod-2); }
    void _pls(ll &x,ll y){ x=((x+y)%mod+mod)%mod; }
    void _pre(){
      mu[1]=pw[0][1]=pw[1][1]=h[1]=p[1]=1;
      for(int i=2;i<=n;i++){
        if(!v[i]){
          v[i]=i; pri[++pri[0]]=i;
          mu[i]=-1;
          pw[1][i]=_pw(i,D);
          pw[0][i]=_pw(i,C)*_inv(pw[1][i])%mod;
          h[i]=((mu[i]+pw[0][i])%mod+mod)%mod;
          p[i]=i;
        }
        for(int j=1;j<=pri[0]&&i*pri[j]<=n;j++){
          v[i*pri[j]]=pri[j];
          pw[0][i*pri[j]]=pw[0][i]*pw[0][pri[j]]%mod;
          pw[1][i*pri[j]]=pw[1][i]*pw[1][pri[j]]%mod;
          if(!(i%pri[j])){
    	mu[i*pri[j]]=0;
    	p[i*pri[j]]=p[i]*pri[j];
    	_pls(h[i*pri[j]],h[i/p[i]]*pw[0][p[i]]%mod*mu[pri[j]]%mod);
    	_pls(h[i*pri[j]],h[i/p[i]]*pw[0][p[i*pri[j]]]%mod);
    	break;
          }
          mu[i*pri[j]]=-mu[i];
          p[i*pri[j]]=pri[j];
          h[i*pri[j]]=h[i]*h[pri[j]]%mod;
        }
      }
      for(int i=1;i<=n;i++){
        h[i]=_inv(h[i]);
        pw[1][i]=_inv(pw[1][i]);
      }
    }
    void _run(){
      flag=0;
      for(int i=1;i<=n;i++){
        b[i]=gi(); f[i]=b[i]*pw[1][i]%mod;
        if(b[i]&&!pw[1][i]) flag=1;
      }   
      for(int i=1;i<=n;i++){
        g[i]=0;
        for(int j=1;j*j<=i;j++)
          if(!(i%j)){
    	_pls(g[i],f[j]*mu[i/j]%mod);
    	if(j*j!=i) _pls(g[i],f[i/j]*mu[j]%mod);
          }
        if(g[i]&&!h[i]){ flag=1; break; }
      }
      if(flag){ puts("-1"); return; }
      for(int i=1;i<=n;i++){
        ll a=0;
        for(int j=i;j<=n;j+=i)
          _pls(a,mu[j/i]*g[j]%mod*h[j]%mod);
        a=a*pw[1][i]%mod;
        printf("%lld ",a);
      }
      putchar('
    ');
    }
    int main(){
    #ifndef ONLINE_JUDGE
      freopen("2.in","r",stdin);
      freopen("x.out","w",stdout);
    #endif
      n=gi(); C=gi(); D=gi(); T=gi();
      _pre();
      while(T--)
        _run();
      return 0;
    }
    
    
    
  • 相关阅读:
    matplotlib实战
    matplotlib常用操作2
    matplotlib 常用操作
    pandas总结
    朴素贝叶斯算法python实现
    什么叫“回归”——“回归”名词的由来&&回归与拟合、分类的区别 && 回归分析
    Latex常用整理
    准备尝试openFrameworks
    常用工具库总结
    K-Means和K Nearest Neighbor
  • 原文地址:https://www.cnblogs.com/BruceW/p/13030432.html
Copyright © 2020-2023  润新知