首页 > 其他分享 >Codeforces 1671 F Permutation Counting 题解

Codeforces 1671 F Permutation Counting 题解

时间:2023-01-08 21:57:11浏览次数:66  
标签:20 题解 LL MOD Permutation 对数 Counting dp 逆序

题目链接

把\(p_i>p_{i+1}\)的位置个数称为间隔数

首先想到一个暴力做法。从小到大挨个添加1-n中的每个数,注意到添加数i时,只能添加到当前序列的最后11个位置中,否则逆序对数就会超。令\(dp_{i,j,p,msk}\)表示当前添加到数i,当前逆序对数为j,间隔数为p,msk是一个集合表示当前序列最后11个位置中哪些满足\(p_i>p_{i+1}\)。转移比较简单。但是这个dp的第一维是n,但n很大。矩阵快速幂也是不行的,后面的维度也很大。

我们考虑把一个长度为n的排列分段。令\(mx_i\)表示\(max_{j=1}^i p_j\)。我们找到从左往右第一个满足\(mx_i=i\)的位置,也就是第一个满足\(p_1\cdots p_i\)是一个排列的位置,然后把1-i分为一段,数组剩下的部分统一减i,然后不断进行同样的分段操作直到序列用完。发现分出的每一段都是一个"小的排列",且左侧的一个小排列中的所有\(p_i\)都小于右侧任意一个小排列中的\(p_i\)。注意到两个段之间不会贡献任何的逆序对数或间隔数,所以排列p的逆序对数和间隔数等于每一小段内部的逆序对数和间隔数之和。

一个小段的长度如果>12,那它的逆序对数就>11,所以此时的p肯定不合法。证明:令小段为一个1-k的排列\(b_1\cdots b_k\),其前缀max为\(c_1\cdots c_k\)。对于任意\(1\le i<k\),满足\(b_i<c_i\)。我们从左往右一个个加入\(b_i\),当加入到i(\(1\le i <k\))时,如果还有满足\(x<b_i\)的x没被加入,那肯定能至少形成一个还没被统计的逆序对;如果不存在这样的x,那肯定存在一个j满足\(j<i,b_j>b_i\)且加入j时有不止一个这样的x,我们现在加入i只是消耗了加入j时产生的一个逆序对而已。综上,总的逆序对个数一定会\(\ge k-1\)。因此我们可以用一个简单的背包算出数组\(f_{i,j,p}\)表示长度为i,逆序对数为j,间隔数为p的小段个数。为了保证对于任意\(1\le i<k\)满足\(b_i<c_i\),dp的时候需要状压一下。

算出f数组之后为了得到n的答案,可能会想到多项式快速幂什么的,其实根本不需要多项式技巧。注意到小段的长度\(>1\)时,逆序对数和间隔数都不为0;而小段长度为1时这两个值都为0。所以长度>1的小段个数\(\le 11\),再做一次背包,然后用插板法把其他长度为1的小段插进去即可。

没仔细算时间复杂度,反正不高。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pdd pair <double,double>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

const LL MOD=998244353;

LL qpow(LL x,LL a)
{
	LL res=x,ret=1;
	while(a>0)
	{
		if(a&1) (ret*=res)%=MOD;
		a>>=1;
		(res*=res)%=MOD;
	}
	return ret;
}

LL t,n,k,x,dp[4100][20][20][20];//dp[msk][k][x][lst]
LL dp2[20][30][20][20];//dp2[cnt][lensum][k][x]

LL rinv[100];
LL C(LL nn,LL mm)
{
  LL ret=1;for(LL i=nn;i>=nn-mm+1;--i) (ret*=i)%=MOD;
  repn(i,mm) (ret*=rinv[i])%=MOD;
  return ret;
}

int main()
{
  fileio();

  rep(i,95) rinv[i]=qpow(i,MOD-2);
  LL lim=12;
  dp[0][0][0][0]=1;
  rep(msk,1<<lim) rep(kk,lim) rep(xx,lim) rep(lst,lim) if(dp[msk][kk][xx][lst])
  {
    LL sz=__builtin_popcount(msk);
    if(msk==(1<<sz)-1&&msk>0) continue;
    LL val=dp[msk][kk][xx][lst],addk=0;
    for(int nxt=lim-1;nxt>=0;--nxt)
    {
      if(msk&(1<<nxt)) ++addk;
      else if(kk+addk<=11) (dp[msk|(1<<nxt)][kk+addk][xx+(int)(lst>nxt)][nxt]+=val)%=MOD;
    }
  }
  rep(i,1<<lim) rep(kk,lim) rep(xx,lim) repn(lst,lim) (dp[i][kk][xx][0]+=dp[i][kk][xx][lst])%=MOD;
  dp2[0][0][0][0]=1;
  rep(i,lim) rep(j,25) rep(kk,lim) rep(xx,lim) if(dp2[i][j][kk][xx])
  {
    LL val=dp2[i][j][kk][xx];
    for(int nxtlen=2;nxtlen<=lim&&j+nxtlen<=24;++nxtlen) rep(kkk,lim-kk) rep(xxx,lim-xx)
    {
      LL msk=(1<<nxtlen)-1;
      if(dp[msk][kkk][xxx][0]==0) continue;
      (dp2[i+1][j+nxtlen][kk+kkk][xx+xxx]+=val*dp[msk][kkk][xxx][0])%=MOD;
    }
  }

  cin>>t;
  rep(tn,t)
  {
    cin>>n>>k>>x;
    LL ans=0;
    rep(cnt,k+1) for(int sz=cnt+cnt;sz<=24;++sz)
    {
      LL val=dp2[cnt][sz][k][x];
      if(val==0||sz>n) continue;
      LL tot=n-sz,spas=cnt+1;
      LL mul=C(tot+spas-1,spas-1);
      (ans+=val*mul)%=MOD;
    }
    cout<<ans<<endl;
  }

  termin();
}

标签:20,题解,LL,MOD,Permutation,对数,Counting,dp,逆序
From: https://www.cnblogs.com/legendstane/p/codeforces-1671-f-Permutation-Counting-solution.html

相关文章

  • 2018年各大赛事题解
    大多数题解都是口胡,不保证正确性,有错请指出,谢谢。CQOI2018除了“交错序列”和“九连环”两道数学题以外,全是板子题,遭不住了。破解D-H协议BSGS板子题,时间复杂度\(\m......
  • Atcoder ABC284 前五题题解
    ABC284A-SequenceofStrings题意:有n个字符串\(s_1,s_2,s_3,...,s_n\),要求按\(n,n-1,n-2,...,1\)的顺序输出样例:输入3TakahashiAokiSnuke输出......
  • Codeforces 1305 F Kuroni and the Punishment 题解 (随机算法)
    题目链接首先注意到每个数最多操作1次就能让他变成2的倍数,所以答案\(\len\)。如果我们能枚举[1,1e12]中所有的质数,并对每个质数p求出把数组中所有数都变成它的倍数的最少......
  • 【题解】P5666 [CSP-S2019] 树的重心
    感觉对重心的理解更直观了一点。题意求一棵树上删去每一条边后两侧子树重心的编号和。\(n\leq3\times10^5\)思路神奇的清真数论。首先这里有一步很妙的操作:把整......
  • LeetCode 887. 鸡蛋掉落-题解分析
    题目来源887.鸡蛋掉落题目详情给你k枚相同的鸡蛋,并可以使用一栋从第1层到第n层共有n层楼的建筑。已知存在楼层f,满足 0<=f<=n,任何从高于f的楼层落......
  • P3829 题解
    题目传送门二维凸包模板传送门题目分析类似于凸包模板的一道题。我们循序渐进地考虑,当半径\(r=0\)时,显然是一个二位凸包模板。接着我们将圆弧加进去,仔细观察发现,我......
  • SYUCT第五次限时训练题解
    第五次限时训练题目大意及ac代码Maxmina题目大意accode#include<iostream>usingnamespacestd;intT,n,m;inta[55];intmain(){cin>>T;whil......
  • Atcoder ABC 284题解
    DHappyNewYear2023(枚举,时间复杂度计算)题意​ 给定\(n\\le\9\times10^{18}\),给出式子\(n=p^2\timesq\),该式子必定有解且有唯一解。请输出\(p\)和\(q\)......
  • Atcoder Beginner Contest ABC 284 Ex Count Unlabeled Graphs 题解 (Polya定理)
    题目链接弱化版(其实完全一样)u1s1,洛谷上这题的第一个题解写得很不错,可以参考直接边讲Polya定理边做这题问题引入:n颗珠子组成的手串,每颗珠子有两种不同的颜色,如果两......
  • Atcoder Beginner Contest ABC 284 Ex Count Unlabeled Graphs 题解 (Polya定理)
    题目链接弱化版(其实完全一样)u1s1,洛谷上这题的第一个题解写得很不错,可以参考直接边讲Polya定理边做这题问题引入:n颗珠子组成的手串,每颗珠子有两种不同的颜色,如果两......