[卡常杯]套套狗

壹、关于题目 ¶

题目背景

“随您怎么说,都由您好了。”他一面走出门道,到楼梯口去,一面说,“只是我得跟您预先声明一下:说不定有人偷听了我们的谈话了,为了避免我们的谈话被人家误解以致闹出什么乱子起见,我得把我们的谈话内容报告校长 —— 把大意说明一下。我不能不这样做。”

题目描述

校长听完很生气,以至于他把他十分拿手的冒泡排序都打错了:

@param p a permutation of 1~n
@param k an arbitrary variable
for(int i=1;i<=k;i++) // the upper limit should be @p n instead of @p k
     for(int j=1;j<n;j++)
         if(p[j]>p[j+1]) swap(p[j],p[j+1]);

不幸的是,校长在使用它对一个 \(1\sim n\)排列 \(\sigma\) 排序时并没有注意到这个问题,以至于祂没有喝上奶茶。

让祂更生气的,是大样例居然没有将这个错误测试出来,这让校长变得十分疯狂,于是就有了「疯狂の校长」,「疯狂の校长」找不到发泄对象,只好把 狗狗 装在套子里面,于是就有了 套套狗,简称 \(\sf TTG\),「疯狂の校长」让一只 \(\sf TTG\) 找到为什么大样例并没有测出祂的错误,这只 \(\sf TTG\) 通过一种 \(\mathcal O(n^3)\) 的算法发现,一种名为 套子 的排列会让「疯狂の校长」喝不上奶茶。具体来说,若一个排列 \(\sigma\) 在进行完以上的冒泡排序之后,存在一个长度为 \(n-1\) 的上升子序列,我们则称 \(\sigma\)套子

由于「疯狂の校长」还想要更多的 套套狗,所以祂想要找到当给定 \(n,k\) 时,祂一共会有多少套子,以便祂抓来数量相当的 狗狗 制作 套套狗,由于这个数字可能会很大,祂只需要你输出 \(\text{Ans}\bmod p\) 的结果,其中 \(p\) 为一给定质数。然而,即使是只知道这个余数,「疯狂の校长」也会知道祂需要多少 狗狗,因为总有 会报告给祂。

数据范围与提示

\(T\) 组测试,对于 \(100\%\) 的数据,满足 \(1\le n,k,T\le 5000,10^8\le p\le 10^9+7\). 然而空间只有 \(\rm 64MB\).

贰、关于题解 ¶

  • 考察反序表,任意一个合法的反序表都唯一对应一个排列,反过来也是一样;
  • 几种情况很明显,分为在 \(k\) 轮后有序,在 \(k\) 轮后仍然无序。
  • 最简单的,若在 \(k\) 轮之后序列有序,即终态反序表全为 \(0\),那么始态反序表最大值不应超过 \(k\),那么反序表最大值排列就是 \(1,\;2,\;3,\;4,\;5,\;\cdots\;k-1,\;k,\;k,\;k,\;\cdots\;k\),把上界加一乘起来就可以了;
  • 若终态无序,那么 \(\rm LIS\) 长度就为 \(n-1\),显然,这种终态只可能有一个无序的数字,我们可以考察这个无序数字的情况:
    • 若该数前有比它大的,那么它之后不可能有比它小的,因为这样 \(\rm LIS\) 显然小于 \(n-1\),再仔细思考一下,这种终态,除了这个数字的反序表有值以外,其他位置都只能为 \(0\). 反推始态,也就是某个位置大于 \(k\),其他位置都小于等于 \(k\),我们枚举这个位置,计算其,然后其他乘起来就可以了;
    • 若该数后有比它小的,那么它前面肯定不能有比它大的,终态一定是一段连续的 \(1\),返回到始态,那就是一段连续的 \(k+1\),我们枚举这个段长就可以了,不过,当段长为 \(1\) 时包含在前面一种情况了,我们不用再重复计算了;
  • 三种情况加起来就完事了;
  • 代码实现,二三先后顺序反着来的,可能有点差异,不过不影响;

叁、关于代码 ¶

# include <bits/stdc++.h>
using namespace std;

// # define NDEBUG
# include <cassert>

namespace Elaina {

# define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
# define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
# define fi first
# define se second
# define mp(a, b) make_pair(a, b)
# define Endl putchar('\n')
# define mmset(a, b) memset(a, b, sizeof (a))
# define mmcpy(a, b) memcpy(a, b, sizeof (a))

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

template <class T> inline T fab(T x) { return x < 0? -x: x; }
template <class T> inline void getmin(T& x, const T rhs) { x=min(x, rhs); }
template <class T> inline void getmax(T& x, const T rhs) { x=max(x, rhs); }
inline char fgetc() {
    # define BUFFERSIZE 5000
    static char BUF[BUFFERSIZE], *p1=BUF, *p2=BUF;
    return p1==p2 && (p2=(p1=BUF)+fread(BUF, 1, BUFFERSIZE, stdin), p1==p2)? EOF: *p1++;
}
template <class T> inline T readin(T x) {
    x=0; int f=0; char c;
    while((c=fgetc())<'0' || '9'<c) if(c=='-') f=1;
    for(x=(c^48); '0'<=(c=fgetc()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
    return f? -x: x;
}
template <class T> inline void writc(T x, char s='\n') {
    static int fwri_sta[55], fwri_ed=0;
    if(x<0) putchar('-'), x=-x;
    do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
    while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
    putchar(s);
}

} using namespace Elaina;

const int maxn=5000;

int n, k, mod;
inline int qkpow(int a, int n) {
    int ret=1;
    for(; n>0; n>>=1, a=1ll*a*a%mod)
        if(n&1) ret=1ll*ret*a%mod;
    return ret;
}

int mn[maxn+5];
inline void solve() {
    n=readin(1), k=readin(1), mod=readin(1);
    if(k>=n) {
        int fac=1;
        rep(i, 1, n) fac=1ll*fac*i%mod;
        writc(fac); return;
    }
    int ans=0, fac=1;
    rep(i, 1, k+1) fac=1ll*fac*i%mod;
    ans=1ll*fac*qkpow(k+1, n-k-1)%mod; // no k+1
    rep(len, 1, n-k-1) { // only one k+1 segment
        int coe=(n-k-1)-len+1;
        ans=(ans+1ll*fac*coe%mod*qkpow(k+1, n-k-1-len)%mod)%mod;
    }
    int mul=1;
    rep(i, 1, n) mn[i]=min(i-1, k), mul=1ll*mul*(mn[i]+1)%mod;
    // one of the number strictly over k+1
    rep(i, 1, n) {
        int res=max(0, i-1-(k+2)+1);
        ans=(ans+1ll*mul*qkpow(mn[i]+1, mod-2)%mod*res%mod)%mod;
    }
    writc(ans);
}

signed main() {
    rep(_, 1, readin(1)) solve();
    return 0;
}
posted @ 2021-09-10 22:31  Arextre  阅读(60)  评论(0编辑  收藏  举报