题解:P16351 「Gensokyo OI Round 1」秘神之钥 / ARIS0_0 - 17
前言
嘟嘟嘟奋战一整场切了 T3 太好力。
有个大佬干到了一个
思路
做法很多。
首先我们定义一个操作序列,由每次操作时左边的数的原编号,当前数的编号,右边的数的编号这个三元组构成。
注意是原编号不是数!
我们用一个
操作过程:
操作序列:
但是注意我们是不可重集合,所以操作序列:
操作过程:
操作序列:
然后我们发现每一次操作以
接下来大力推式子和猜结论。
考虑到我们无论如何,最终的大小关系和
你发现题目中的限制是至少一个,然后总方案是固定的
于是直接正难则反,求一个所有条件都不满足的方案数。
正难则反过后的限制很强,不妨设
你发现对于偶数每个数的限制要么比
不妨设限制是大于
那么你的方案数是
大体意义是,你从
当
做到这里卡了我很久,所以这里大概是一个重点,我推了接近一整场才发现的规律。
这里不要去想那个
设
我们考虑枚举
设
含义是你发现你比
接下来你在你取出来的除了
最后,这
这样代码就好写了。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define piii pair<pii, int>
#define fi first
#define se second
const int N = 1e6 + 5;
namespace ARIS0_0{
int n, mod;
int p[N], fac[N], inv[N];
bool vis[N], vis1[N], vis2[N];
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod, b >>= 1;
}
return res;
}
int C(int n, int m){
if (n < m) return 0;
return 1ll * fac[n] * inv[n - m] % mod * inv[m] % mod;
}
void init(){
}
void solve(){
cin >> n >> mod;
if (n % 2 == 0){
for (int i = 1; i <= n; i ++ ) vis[i] = 0;
fac[0] = 1;
for (int i = 1; i <= n; i ++ ) fac[i] = 1ll * fac[i - 1] * i % mod;
inv[n] = qpow(fac[n], mod - 2);
for (int i = n - 1; i >= 0; i -- ) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
int all = fac[n];
for (int i = 1; i <= n; i ++ ) p[i] = i;
int x = 0, y = 0;
for (int i = 1; i <= n; i ++ ){
int l = (i == 1 ? n : i - 1);
int r = (i == n ? 1 : i + 1);
if (p[i] == 1){
if (p[l] == n) swap(l, r);
if (!vis[p[l]]) x ++, vis[p[l]] = 1;
}
else{
if (p[r] == 1) swap(l, r);
if (!vis[p[r]]) y ++, vis[p[r]] = 1;
}
swap(p[l], p[r]);
}
int tmp = 2ll * ((C(n, x + 1) + mod - 1) % mod) % mod * fac[x] % mod * fac[y] % mod;
cout << ((all - tmp) % mod + mod) % mod << '\n';
}
else{
for (int i = 1; i <= n; i ++ ) vis1[i] = vis2[i] = 0;
fac[0] = 1;
for (int i = 1; i <= n; i ++ ) fac[i] = 1ll * fac[i - 1] * i % mod;
inv[n] = qpow(fac[n], mod - 2);
for (int i = n - 1; i >= 0; i -- ) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
int all = fac[n];
for (int i = 1; i <= n; i ++ ) p[i] = i;
int x = 0, y = 0, z = 0;
for (int i = 1; i <= n; i ++ ){
int l = (i == 1 ? n : i - 1);
int r = (i == n ? 1 : i + 1);
if (p[i] == 1){
if (p[l] == n) swap(l, r);
vis1[p[l]] = 1;
}
else{
if (p[r] == 1) swap(l, r);
vis2[p[r]] = 1;
}
swap(p[l], p[r]);
}
for (int i = 1; i <= n; i ++ ){
if (vis1[i] && vis2[i]) y ++;
if (vis1[i]) x ++;
if (vis2[i]) z ++;
}
int tmp = 0;
for (int i = 0; i <= x - y; i ++ ){
int t = x - y - i;
tmp = (tmp + 1ll * C(n - t - 1, y + i + 1) * C(y + i, y) % mod * fac[y] % mod * fac[x - y] % mod * fac[n - x - 2] % mod) % mod;
}
tmp = 2ll * tmp % mod;
cout << ((all - tmp) % mod + mod) % mod << '\n';
}
}
void single(){ init(), solve(); }
void multi(){ init(); int T; cin >> T; while (T -- ) solve(); }
void idmulti(){ init(); int id, T; cin >> id >> T; while (T -- ) solve(); }
};
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
ARIS0_0::multi();
}
// 可能南方的阳光,照着北方的风。
// 可能时光被吹走,从此无影无踪。
// 跑题了,没关系。祝好
// ARIS0_0 - 17