质数前缀积构造难题解析

题目链接:https://www.luogu.com.cn/problem/P3599  


这道题算是一道比较难的构造题,主要是第二种构造难度相对较高.

X=1的构造:

要让数列前缀和sum在模n意义下不同,我们发现如果n放在第i个,则有sum_i-sum_{i-1}=n\implies sum_i \equiv sum_{i-1}(mod\ n),i>1,此时不满足各不相同,因此n必然放在第1个,即sum_1\equiv 0(mod\ n). 同时,由于sum_n=\frac{n(n+1)}{2},因此若n为奇数时(除1外),必然有sum_1\equiv sum_n\equiv 0(mod\ n),故将该情况排除;考虑n为偶数时的构造:我们可以让前缀和依次为0,-1,1,-2,2…即可,此时各数即为0,-1,2,-3,4…即a_i=(-1)^{i-1}(i-1),此时可以满足条件.

这组构造相对简单,我们主要考虑下面情况;

X=2的构造:

要让数列前缀积accu在模n意义下不同,n必然在最后,否则其之后的前缀积全为0. 若n为合数,则必然可分解为n=ab,a<n,b<n,即必然会使出现二者之后前缀积全为0,排除此情况. 考虑n为质数时,我们不妨令前缀积依次为1,2,3,4…n,则

a_n=\begin{cases} 1,n=1\\ \frac{n}{n-1},n>1 \end{cases},由于n为质数,可通过费马小定理求其逆元(当然也可以线性递推). 

注意:n=4要特判,因为4=2×2,无法分解为两不同小于4的数的乘积,可以给出构造。

两种情况n=1都直接输出“2 1”即可。


代码实现
#include<bits/stdc++.h>
using namespace std;
#define ll long long//记得开long long,不然快速幂会爆int
ll const N=1e5+10;
ll X,T,n;
namespace locax{
	inline ll check(ll n){
		for(ll i=2;i*i<=n;i++){
			if(n%i==0)return 1;
		}
		return 0;
	}
	inline ll qukpow(ll a,ll b){
		if(b==1)return a;
		ll res=qukpow(a,b/2);
		if(b&1)return (((res*res)%n)*a)%n;
		else return (res*res)%n;
	}
	inline void solve1(ll n){
		if(n==1){
			cout<<"2 1\n";return;
		}
		if(n&1){
			cout<<"0\n";return;
		}
		cout<<"2 ";
		for(ll i=1;i<=n;i++){
			if(i&1)cout<<n+1-i<<' ';
			else cout<<i-1<<' ';
		}
		cout<<'\n';
	}
	inline void solve2(ll n){
		if(n==1){
			cout<<"2 1\n";return;
		}
		if(n==4){
			cout<<"2 1 3 2 4\n";
			return;
		}
		if(check(n)){
			cout<<"0\n";
			return;
		}
		cout<<"2 1 ";
		for(ll i=2;i<n;i++){
			cout<<(i*qukpow(i-1,n-2))%n<<' ';
		}
		cout<<n<<'\n';
	}
}
int main(){
	cin>>X>>T;
	if(X==1){
		while(T--){
			cin>>n;locax::solve1(n);
		}
	}
	else{
		while(T--){
			cin>>n;locax::solve2(n);
		}
	}
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值