zoukankan      html  css  js  c++  java
  • UOJ 【UR #5】怎样跑得更快

    【UOJ#62】怎样跑得更快

    题面

    这个题让人有高斯消元的冲动,但肯定是不行的。

    这个题算是莫比乌斯反演的一个非常巧妙的应用(不看题解不会做)

    套路1:

    因为(b(i))能表达成一系列(x(i))的和,所以我们尝试通过反演将(x(i))表达成一系列(b(i))的和的形式,那么就可以解出来了。

    然后一个简单的化简:(gcd(i,j)^ccdot lcm(i,j)^d=i^dcdot j^dcdot gcd(i,j)c-d)

    [displaystyle b_i=sum_{j=1}^ni^dj^dgcd(i,j)^{c-d}x_j\ frac{b_i}{i^d}=sum_{j=1}^ngcd(i,j)^{c-d}j^dx_j ]

    套路2:

    看到gcd非常不爽,考虑干掉它。

    我们设(f(i)=i^{c-d}),再构造函数(fr(i)),使得(f(n)=sum_{d|n}fr(d))(d|gcd(i,j)Rightarrow d|i,d|j)

    由莫比乌斯反演可以得到(fr(n)=sum_{d|n}mu(d)f(frac{n}{d}))

    所以

    [displaystylefrac{b_i}{i^d}=sum_{j=1}^nsum_{d|i,d|j}fr(d) j^dx_j\ ]

    然后我们要将右边的式子交换求和符号(因为我们要得到套路1的形式)。

    [displaystylefrac{b_i}{i^d}=sum_{j=1}^nsum_{d|i,d|j}fr(d) j^dx_j\ =sum_{d|i}fr(d)sum_{d|j}j^dx_j\ ]

    套路3:

    我们设(Z_d=sum_{d|j}j^dx_j)

    于是(displaystylefrac{b_i}{i^d}=sum_{d|i}fr(d)Z_d),然后我们呢将(fr(d))解出来了过后就可以将(Z_d)解出来。

    又是莫比乌斯反演

    [displaystyle Z_d=sum_{d|j}j^dx_j\ j^dx_j=sum_{j|d}mu(frac{d}{j})Z_j ]

    三个反演,就将这道好题(毒瘤题)解决了。

    总结:

    反演在两个函数直接构建了转换的桥梁,遇到难以求解的函数时,通常将其反演成一个好求的函数再反演回来。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 100005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,c,d,q;
    ll f[N],b[N],g[N],h[N];
    ll fr[N],z[N];
    ll s[N],ans[N];
    const ll mod=998244353;
    ll ksm(ll t,ll x) {
    	ll ans=1;
    	for(;x;x>>=1,t=t*t%mod)
    		if(x&1) ans=ans*t%mod;
    	return ans;
    }
    bool vis[N];
    int pri[N],mu[N];
    
    void pre(int n) {
    	mu[1]=1;
    	for(int i=2;i<=n;i++) {
    		if(!vis[i]) {
    			pri[++pri[0]]=i;
    			mu[i]=-1;
    		}
    		for(int j=1;j<=pri[0]&&1ll*i*pri[j]<=n;j++) {
    			vis[i*pri[j]]=1;
    			if(i%pri[j]==0) {
    				break;
    			}
    			mu[i*pri[j]]=-mu[i];
    		}
    	}
    }
    
    ll tem[N];
    void solve() {
    	for(int i=1;i<=n;i++) {
    		if(fr[i]==0&&s[i]) {cout<<-1<<"
    ";return ;}
    		z[i]=s[i]*ksm(fr[i],mod-2)%mod;
    	}
    	for(int i=1;i<=n;i++) tem[i]=0;
    	for(int i=1;i<=n;i++) {
    		for(int j=i;j<=n;j+=i) {
    			(tem[i]+=mu[j/i]*z[j]+mod)%=mod;
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		if(!h[i]&&tem[i]) {cout<<-1<<"
    ";return ;}
    		ans[i]=tem[i]*ksm(h[i],mod-2)%mod;
    	}
    	for(int i=1;i<=n;i++) cout<<ans[i]<<" ";cout<<"
    ";
    }
    
    int main() {
    	n=Get(),c=Get(),d=Get(),q=Get();
    	pre(n);
    	int t=c-d;
    	t=(t%(mod-1)+mod-1)%(mod-1);
    	for(int i=0;i<=n;i++) f[i]=ksm(i,t);
    	for(int i=1;i<=n;i++) {
    		h[i]=g[i]=ksm(i,d);
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j+=i)
    			(fr[j]+=mu[j/i]*f[i]+mod)%=mod;
    	while(q--) {
    		for(int i=1;i<=n;i++) {
    			s[i]=0;
    			b[i]=Get();
    			b[i]=b[i]*ksm(g[i],mod-2)%mod;
    		}
    		for(int i=1;i<=n;i++) 
    			for(int j=i;j<=n;j+=i) 
    				(s[j]+=mu[j/i]*b[i]+mod)%=mod;
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    iOS 2D绘图 (Quartz2D)之路径(点,直线,虚线,曲线,圆弧,椭圆,矩形)
    iOS 2D绘图 (Quartz 2D) 概述
    HTML 学习笔记 JavaScript(创建对象)
    iOS NSFileManager 使用详解
    iOS 中 const static extern 关键字总结
    Tornado WEB服务器框架 Epoll
    Windows 数据盘自动分区脚本
    跨域请求测试代码-图片视频自动播放
    mail如何在linux中发送邮件,使用163邮箱发信。
    Linux系统CPU频率调整工具使用
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10009418.html
Copyright © 2011-2022 走看看