2018.11.17【NOIP练习】zxyoi的项链(数学推理)(线性筛)(欧拉函数)(链表)(Polya原理)...

传送门


解析:

zxyoi完成oi生涯中的第一次出题。

思路:

首先肯定是要推结论的。

令询问为nnnmmm时的答案为N(n,m)N(n,m)N(n,m)
首先断环成链,同一条项链考虑mmm种断开的方式,那么得到的链的数量就有mN(n,m)mN(n,m)mN(n,m)。每个位置任意填得到的答案为nmn^mnm,其中必然有重复。

考虑序列a1a2...ama_1a_2...a_ma1a2...am会出现多少次,显然就是产生的循环位移ak..ama1...ak−1a_k..a_ma_1...a_{k-1}ak..ama1...ak1有多少与原串相同。
SnS_nSn表示颜色集合,则有mN(n,m)=∑k=1m∑a1,a2,a3...am∈Sn[a1a2...am=ak...ama1...ak−1]mN(n,m)=\sum_{k=1}^{m}\sum_{a_1,a_2,a_3...a_m\in S_n}[a_1a_2...a_m=a_k...a_ma_1...a_{k-1}]mN(n,m)=k=1ma1,a2,a3...amSn[a1a2...am=ak...ama1...ak1]

然后就很妙了。考虑当m=6m=6m=6k=4k=4k=4的时候,如何计算a1a2a3a4a5a6=a4a5a6a1a2a3a_1a_2a_3a_4a_5a_6=a_4a_5a_6a_1a_2a_3a1a2a3a4a5a6=a4a5a6a1a2a3有多少组解?

显然a1=a4,a2=a5,a3=a6a_1=a_4,a_2=a_5,a_3=a_6a1=a4,a2=a5,a3=a6,所以实际上能够随意填的只有3=gcd(m,k−1)3=gcd(m,k-1)3=gcd(m,k1)个位置,解的数量为ngcd(m,k−1)n^{gcd(m,k-1)}ngcd(m,k1)

严格的叙述为,考虑(m,k)(m,k)(m,k)的解的限制。无非就是aj=a(j+k−1)%m+1a_j=a_{(j+k-1)\%m+1}aj=a(j+k1)%m+1

d=gcd(m,k−1)d=gcd(m,k-1)d=gcd(m,k1),则所有的a(t+s×d)%m+1a_{(t+s\times d)\%m+1}a(t+s×d)%m+1都是被锁死在了一起,能够随意选择的只有a1,a2...ada_1,a_2...a_da1,a2...ad一共ddd个数,则解的数量为ngcd(m,k−1)n^{gcd(m,k-1)}ngcd(m,k1)

以上,我们证明了mN(n,m)=∑k=1mngcd(m,k−1)=∑k=0m−1ngcd(m,k)mN(n,m)=\sum_{k=1}^{m}n^{gcd(m,k-1)}=\sum_{k=0}^{m-1}n^{gcd(m,k)}mN(n,m)=k=1mngcd(m,k1)=k=0m1ngcd(m,k)

但是这个式子还需要化简。实测没化简过不了。

N(n,m)=1m∑d∣mnd∑k=0m−1[gcd(k,m)=d]N(n,m)=\frac{1}{m}\sum_{d|m}n^d\sum_{k=0}^{m-1}{[gcd(k,m)=d]}N(n,m)=m1dmndk=0m1[gcd(k,m)=d]=1m∑d∣mnd∑k=0m−1[gcd(k/d,m/d)=1]=\frac{1}{m}\sum_{d|m}n^d\sum_{k=0}^{m-1}[gcd(k/d,m/d)=1]=m1dmndk=0m1[gcd(k/d,m/d)=1]=1m∑d∣mnd∑k=0m/d−1[gcd(k,m/d)=1]=\frac{1}{m}\sum_{d|m}n^d\sum_{k=0}^{m/d-1}[gcd(k,m/d)=1]=m1dmndk=0m/d1[gcd(k,m/d)=1]

所以最终得到的式子为N(n,m)=1m∑d∣mndφ(m/d)N(n,m)=\frac{1}{m}\sum_{d|m}n^d\varphi(m/d)N(n,m)=m1dmndφ(m/d)

线性筛同时处理欧拉函数和因数表就行了。

这里因数表的处理有很多方法,我没有刻意去卡其他做法,询问数可以再大一点,可能就需要用这种方法了。

我这里用的是类似链表的方式,可以在O(∑σ0(i))O(\sum\sigma_0(i))O(σ0(i))的复杂度内处理出所有因数表。其中σ0(i)\sigma_0(i)σ0(i)表示iii的因数个数。

质数显然直接存就行了。

一个合数BBB必然由质数PPP和另一个数AAA相乘得到,那么AAA的所有因子必然都是BBB的因子,那么BBB的表末端直接连在AAA的表末端就好了。然后考虑每个AAA的因子δ\deltaδ,首先δ×P\delta\times Pδ×P必然是BBB的因子,但是也有可能是AAA的因子,这个直接判断一下再插入BBB的链表末端就行了。

这样大部分因数表是由相当一部分数共享的,可以优化空间。


UPDUPDUPD

抱歉,这是一道Polya的裸题


代码(已删去所有常数优化,可过):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define cs const

cs int P=500005;
cs int mod=1000000007;

int prime[P],pcnt;
bool mark[P];
vector<int> factor;
vector<int> pre;
int last[P],phi[P],inv[P];

void _copy(int goal,int f,int p){
	bool flag=false;
	for(int i=last[f];~i;i=pre[i]){
		int v=factor[i];
		if(f%(v*p)){
			if(flag)
			pre.push_back(factor.size()-1); 
			else pre.push_back(last[f]),flag=true;
			factor.push_back(v*p);
		}
	}
	last[goal]=pre.size()-1;
}

void linear_sieves(int len=P-5){
	last[1]=0;phi[1]=1;
	factor.push_back(1);
	pre.push_back(-1);
	for(int i=2;i<=len;++i){
		if(!mark[i]){
			prime[++pcnt]=i;
			phi[i]=i-1;
			_copy(i,1,i);
		}
		for(int j=1;j<=pcnt&&i*prime[j]<=len;++j){
			mark[i*prime[j]]=true;
			if(i%prime[j]==0){
				phi[i*prime[j]]=phi[i]*prime[j];
				_copy(i*prime[j],i,prime[j]);
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
			_copy(i*prime[j],i,prime[j]);
		}
	}
}

int quickpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=(ll)ans*a%mod;
		b>>=1;
		a=(ll)a*a%mod;
	}
	return ans;
}

int query(int n,int m){
	int ans=0;
	for(int i=last[m];~i;i=pre[i]){
		ans=((ll)ans+(ll)quickpow(n,factor[i])*phi[m/factor[i]])%mod;
	}
	return (ll)ans*inv[m]%mod;
}

int T;
signed main(){
	linear_sieves();
	inv[1]=inv[0]=1;
	for(int i=2;i<=P-5;++i){
		inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
	}
	scanf("%d",&T);
	while(T--){
		int n,m;
		scanf("%d%d",&n,&m); 
		printf("%d\n",query(n,m));
	}
	return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047089.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值