P3599 Koishi Loves Construction(构造+思维)

18 篇文章 0 订阅

P3599 Koishi Loves Construction

超级考验思维的构造题.

题意:

构造出这两种:

  1. 长度n的n的全排列,使得前缀和 m o d n modn modn后数各不相同.
  2. 长度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 ni+1
  • 偶数位置放 i − 1 i-1 i1

说完前缀和我们来看前缀积:
前缀知识:逆元
我们知道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 (i1)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;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值