UOJ 【UR #5】怎样跑得更快

【UOJ#62】怎样跑得更快

题面

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

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

套路1:

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

然后一个简单的化简:\(gcd(i,j)^c\cdot lcm(i,j)^d=i^d\cdot j^d\cdot 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})\)

所以

\[\displaystyle\frac{b_i}{i^d}=\sum_{j=1}^n\sum_{d|i,d|j}fr(d) j^dx_j\\ \]

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

\[\displaystyle\frac{b_i}{i^d}=\sum_{j=1}^n\sum_{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\)

于是\(\displaystyle\frac{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<<"\n";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<<"\n";return ;}
		ans[i]=tem[i]*ksm(h[i],mod-2)%mod;
	}
	for(int i=1;i<=n;i++) cout<<ans[i]<<" ";cout<<"\n";
}

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;
}
posted @ 2018-11-23 19:40  hec0411  阅读(170)  评论(0编辑  收藏  举报