[链接] http://codeforces.com/problemset/problem/487/C
[sol]
显然,n需要放到最后一个位置上,不然的话后面若干个乘积%p均都为0,与题意相违背。当n为合数时,n
一定能写成一个素数和合数的乘积(4除外),所以除了4以外的合数都没有合法方案。
当n为素数时,令a[i] = i / i-1 % n=i* inv[i - 1](i - 1的逆元);那么形成的序列就是1,2,3,…n-1,0,并且
a[i]=a[j]当且仅当i=j时成立,证明如下:
i * inv[i - 1] = j * inv[j - 1]
同乘(i - 1) * (j - 1)得:
i * (j - 1) = j * (i - 1)
i = j;
关于逆元的求法,此题需要预处理n以内的逆元,一般用O(n)通过下面这个公式来搞
inv[i] = (p - p / i)*inv[p % i] % p;
证明的话就是先化简,然后同乘i* (p % i)即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 100000 + 5;
ll inv[N];
bool ok(int x)
{
for(int i = 2; i <= sqrt(x); i++)
if (x % i == 0)
return false;
return true;
}
int main()
{
int n;
scanf("%d", &n);
if (n == 1)
{
printf("YES\n1");
return 0;
}
if (n == 4)
{
printf("YES\n1\n3\n2\n4\n");
return 0;
}
if (!ok(n))
{
printf("NO\n");
return 0;
}
inv[1] = 1;
printf("YES\n1\n");
for(int i = 2; i < n; i++)
{
inv[i] = 1LL * (n - n / i) * inv[n % i] % n;
cout<<i * inv[i - 1] % n<<endl;
}
printf("%d\n", n);
return 0;
}
[后话]
本题本来想通过两个限制条件的全排列来搞,这样的话复杂度不知道能优化到哪个程度,事实证明
(n<20)时勉强可以出界
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100000 + 5;
bool f[N], v[N];
int q[N];
int n;
void dfs (int dep,int ans)
{
if (dep == n - 1)
{
printf("-----------------------------------------\n");
for(int i = 1; i < n; i++)
printf("%d\n", q[i]);
printf("-----------------------------------------\n");
return;
}
for(int i = 1; i < n; i++)
if(!f[i])
{
int t = ans * i % n;
if (v[t] || t>=n)
continue;
f[i] = true;
v[t] =true;
q[dep + 1] = i;
dfs(dep + 1,t);
f[i] = false;
v[t] = false;
q[dep + 1] = 0;
}
}
int main()
{
v[0] = true;
scanf("%d", &n);
dfs(0,1);
return 0;
}