原题链接:P3599 Koishi Loves Construction - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:给定n,求一组排列,满足前n个的前缀和 mod n 的结果不同,或者前n个前缀积 mod n 的结果不同
思路
前缀和
首先前缀和,蠢货都看不到排列两个字
容易想到,要mod n 的结果不同,最好就是每两项结果互补,也就是让结果与 n 值 +1, -1,+2,-2……
这样就可以得到各不相同的一组排列
再发现如果n是奇数,前缀和结果是n的倍数,这样mod n结果为0的情况会出现两次,没法构造满足条件的排列,同时,排列的 n值 只能出现在最前,否则另一个数加n mod n 结果相同
得到前缀和的构造
第一位: n ,偶数位:1,3,5…… idx-1,奇数位:n-1-1, n-2-2…… n-idx+1
前缀积
首先结果不能是n的倍数,否则mod n 结果为0出现两次,最后一项肯定是n,否则n值出现之后,其后的前缀积结果都是0
很难发现其实要求逆元, 这是mod n 同余 i 的问题,每一项前缀积结果要不相同,可以人为定义每一项的结果从1,2,…… n-1,0
这样一来,每次的新乘数,就可以是 当前前缀积 的mod n 余 i 的逆元,也就是等于 mod n余1的逆元乘以i
可以发现这样可以保证每次结果不同,但是能否保证这是一组排列呢?至少可以mod n来保证数不会超过n
其余暂时证明不了
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long LL;
LL n;
void solve_1()
{
//判断能否成立
cin >> n;
//特判1
if(n & 1 && n != 1)
{
puts("0");
return;
}
cout << "2 ";
//在0上下浮动 0 1 -2 3 -4 这样就可以实现0 1 n-1 2 n-2……
for(LL i = 1; i <= n; i ++)
{
if(i & 1) cout << n + 1 - i;
else cout << i - 1;
if(i < n) cout << ' ';
else cout << endl;
}
}
//求逆元前要判断素数
bool np[N];
LL prime[N];
void get_prime()
{
LL cnt = 0;
for(LL i = 2; i <= N; i ++)
{
if(!np[i]) prime[cnt ++] = i;
for(LL j = 0; prime[j] <= N / i; j ++)
{
//用最小的质数删去其倍数
np[prime[j] * i] = true;
if(i % prime[j] == 0) break;
}
}
}
LL qmi(LL a, LL k)
{
LL ans = 1;
while(k)
{
if(k & 1) ans = ans * a % n;
k >>= 1;
a = a * a % n;
}
return ans;
}
void solve_2()
{
cin >> n;
if(np[n] && n != 1 && n != 4)
{
puts("0");
return;
}
cout << "2 ";
if(n == 1)
{
cout << "1" << endl;
return;
}
if(n == 4)
{
cout << "1 3 2 4" << endl;
return;
}
for(LL i = 1, ans = 1, sum = ans; i <= n - 1; i ++)
{
//求逆元
cout << ans << ' ';
ans = qmi(sum, n - 2) * (i + 1) % n; //因为是下一次的输出,所以i+1
sum = ans * sum % n;
}
cout << n << endl;
}
int main()
{
LL x, t;
cin >> x >> t;
if(x == 1)
while(t --)
{
solve_1();
}
else
{
get_prime();
while(t --)
{
solve_2();
}
}
return 0;
}