P3599 Koishi Loves Construction
超级考验思维的构造题.
题意:
构造出这两种:
- 长度n的n的全排列,使得前缀和 m o d n modn modn后数各不相同.
- 长度n的n的全排列,使得前缀积 m o d n modn modn后数各不相同.
思路:
先看前缀和的:
而(x+n)%n==x,所以n一定是放在第一个位置的,
首先我们看当n为奇数时,
那么n-1就是偶数 :那么 1+(n-1) ,2+(n-2)…都等于n.
那么最后和一定是n的倍数.也就会再出现个取余为0.
所以奇数的话(除去1)不行
偶数的话:
因为我们将n固定到最前面.
我们可以想到最简单的是让前缀和取膜之后变成(0,1,2,3,4,…)
但是我们不能构造不出来这样的,但是我们可以构造出(0(n),1,n-1, 2,n-2,3,n-3…)相当于(0,1,-1,2,-2,3…)
通过这个来重新构造出各个位上的数字就是(0,1,-2,3,-4,…)
第一个位置放上n构造出0,第二个位置想构造出1,那么将1添加进去,那么在想构造出2就有点不可能了,但是我们可以构造出
−
1
-1
−1那么我们就顺势将n-3加进去,我们下一步就可以构造出3将2加进去.…
所以我们最后就能顺势构造出取膜的数组为(0,1,-1,2,…)
之后我们可以反过来推我们n的全排列
就是:
- 奇数位置放 n − i + 1 n-i+1 n−i+1
- 偶数位置放 i − 1 i-1 i−1
说完前缀和我们来看前缀积:
前缀知识:逆元
我们知道x*n%n=0,那么后面的在乘任何数都是0,所以n放在最后.
首先我们跟上面一样,排除一些不行的n,当n不是素数且不是4和1时,那么他就有因子(除了n本身)乘积等于n.那么在到达最后之前就会乘积是n的倍数,到最后就一定有重复的0.
排除这个之后我们再看为什么4可以:
4的话(1,2,3,4) 其中因子(除了4)外,只有2 * 2但是2只能用一次所以不会在到达最后之前产生4的倍数.
1自然就不用说了,直接输出就行了.
再看符合条件的:质数.
我们还像上面分析的一样想要取膜数组为(0,1,2,3,…),但是由于乘积问题,所以我们将n放在最后,把1放在最前面(任何数乘1都是这个数那么取膜后不变只能放在第一位),那么我们就将取膜数组变换成(1,2,3,…0)
根据这个变化得:
当前缀取膜后是(i-1)时:那么:
(
i
−
1
)
∗
a
[
i
]
=
=
i
m
o
d
n
(i-1)*a[i]==i mod n
(i−1)∗a[i]==imodn
因为我们前面说了n是质数,那么我们大可以利用逆元来求(a[i]);
#include <stack>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
typedef unsigned long long ull;
#define x first
#define y second
#define sf scanf
#define pf printf
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
#define debug(x) cout << #x << ": " << x << endl;
#define lowbit(x) ((-x)&x)
const int MOD = 1000;
///const int mod = 998244353;
const int maxn = 1e5 + 10;
const int dx[] = {0, 0, 1, -1, 0, 0 };
const int dy[] = {0, 0, 0, 0, 1, -1 };
const int dz[] = {1, -1, 0, 0, 0, 0 };
ll n, m, l, T, sum, ans, cnt, k, flag;
string str[500];
string s;
ll b[maxn], a[maxn];
void prime()
{
for(int i = 2; i < maxn; i++)
{
if(!a[i])
{
b[++cnt] = i;
}
for(int j = 1; j <= cnt && b[j]*i < maxn; j++)
{
a[i * b[j]] = 1;
if(i % b[j] == 0) break;
}
}
}
ll qpow(ll a, ll b, ll p)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % p;
b >>= 1;
a = a * a % p;
}
return ans;
}
void check1(ll n)
{
///0 1 -2 3 -4 5
if(n % 2 == 0 || n == 1)
{
printf("2 ");
for(ll i = 1; i <= n; i++)
{
if(i % 2)
{
printf("%lld ", n - i + 1);
}
else
{
printf("%lld ", i - 1);
}
}
cout << endl;
}
else
{
puts("0");
}
}
void check2(ll n)
{
if(n == 1) puts("2 1");
else if(n == 4) puts("2 1 3 2 4");
else if(!a[n])
{
printf("2 ");
for(int i = 1, tmp = 1, ans = 1; i < n; i++)
{
printf("%d ", tmp);
tmp = 1ll * qpow(ans, n - 2, n) * (i + 1) % n;
ans = 1ll * tmp * ans % n;
}
printf("%lld\n", n);
}
else
{
puts("0");
}
}
void solve()
{
cin >> n >> m;
while(m--)
{
ll x;
cin >> x;
if(n == 1)
{
check1(x);
}
else
{
check2(x);
}
}
}
int main()
{
ll t = 1;
///scanf("%lld",&t);
prime();
while(t--)
{
solve();
}
return 0;
}