当前位置:网站首页>[NOI2018] 冒泡排序(组合+卡特兰数+dp+树状数组)
[NOI2018] 冒泡排序(组合+卡特兰数+dp+树状数组)
2022-07-27 19:57:00 【迷蒙之雨】
洛谷题目传送门
8pts
n!枚举排列,然后判断即可
44pts
首先考虑什么样的序列是符合条件的
题目提示启发我们,可以考虑每个元素的移动
因为总交换次数是要达到下界,所以每一次交换都必须是有益的
考虑如果排列第i个位置是p[i]
若 p [ i ] = i p[i]=i p[i]=i,则这位置不能被交换
若 p [ i ] < i p[i]<i p[i]<i,则这个数需要往左移动,且不能向右移动
那么不能存在一个j满足, j > i , p [ j ] < p [ i ] j>i,p[j]<p[i] j>i,p[j]<p[i],否则这两个位置一定会交换一次,那么 p [ i ] p[i] p[i]就向相反方向移动了,答案一定会变大,即 i i i右边的数都比 p [ i ] p[i] p[i]大
若 p [ i ] > i p[i]>i p[i]>i,类似的可以得到i左边的数都比 p [ i ] p[i] p[i]小
且这三个限制和原限制是等价的
直接状压选了那些数字即可
字典序的限制可以类似数位dp去搞
复杂度 O ( 2 n n ) O(2^nn) O(2nn)
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5+7;
typedef long long LL;
const int mod = 998244353;
int n,a[N];
LL f[1<<20][2];
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int m=(1<<n);
memset(f,0,sizeof(f));
f[0][1]=1;
for(int s=0;s<m-1;s++) {
int p=0; int a1=0,a2=n+1; for(int i=1;i<=n;i++) if((s>>(i-1))&1)
{
p++;
a1=max(a1,i);
}
else a2=min(a2,i);
p++;
for(int limit=0;limit<=1;limit++) {
int up=(limit?a[p]:1); for(int i=up;i<=n;i++) {
int k=(limit&&(i==up)); if(!((s>>(i-1))&1))
{
if(i>=p)
{
if(a1<i)
f[s+(1<<(i-1))][k]=(f[s+(1<<(i-1))][k]+f[s][limit])%mod;
}
else
{
if(i<=a2)
f[s+(1<<(i-1))][k]=(f[s+(1<<(i-1))][k]+f[s][limit])%mod;
}
}
}
}
}
printf("%lld\n",f[m-1][0]);
}
int main()
{
int T;
cin>>T;
while(T--)
{
solve();
}
return 0;
}
80pts
一个重要的结论:
不考虑字典序限制
题目所求等价于求有多少个排列,满足排列中不存在长度大于等于3的下降子序列
必要性:
即若该排列合法,则不存在长度大于等于3的下降子序列
若不然,则存在三个数 i , j , k i,j,k i,j,k满足, i < j < k , p [ i ] > p [ j ] > p [ k ] i<j<k,p[i]>p[j]>p[k] i<j<k,p[i]>p[j]>p[k]
若 j < p [ j ] j<p[j] j<p[j],由上文可知,j左边的数都比j小,与 p [ i ] > p [ i ] p[i]>p[i] p[i]>p[i]矛盾
若 j > p [ j ] j>p[j] j>p[j],则j右边的数都比j大,与 p [ j ] > p [ k ] p[j]>p[k] p[j]>p[k]矛盾
所以矛盾,即原命题成立
充分性
即若不存在 p [ i ] > p [ j ] > p [ k ] p[i]>p[j]>p[k] p[i]>p[j]>p[k],则性质成立
考虑一次交换,交换了 p [ i ] , p [ i + 1 ] p[i],p[i+1] p[i],p[i+1]
则由题可知, p [ i ] > p [ i + 1 ] p[i]>p[i+1] p[i]>p[i+1]
又因为没有长度大于等于3的下降子序列,所以i左边的数都小于 p [ i ] p[i] p[i]
那么就有 p [ 1 … … i − 1 ] p[1……i-1] p[1……i−1]和 p [ i + 1 ] p[i+1] p[i+1]总共i个数比 p [ i ] p[i] p[i]小,即 p [ i ] > i p[i]>i p[i]>i
类似的我们可以得到 p [ i + 1 ] < i + 1 p[i+1]<i+1 p[i+1]<i+1,那么这次交换对最终排列而言是合法的
所以命题成立
即题目等价于求有多少的排列,满足排列中不存在长度大于等于3的下降子序列
考虑dp
设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示有多少个长度为i的排列,满足第一个元素是j,且不存在长度大于等于3的下降子序列,则由定义知 j ≤ i j\leq i j≤i
考虑第二个数填k
若 j < = k j<=k j<=k,则加上dp[i-1][k],这里之所以能等于j,而不会重复,是因为虽然整个序列是一个排列,但是每一个子段并不是一个排列,而我们之所以能转移,是因为我们只考虑元素的相对关系,即离散化之后的值,那么长度为i的数列,长度为i-1的数列,离散化之后有值相等是显然可以的
若 1 < k < j 1<k<j 1<k<j,那么一定会存在一个 j , k , 1 j,k,1 j,k,1的不合法,下降子序列,因此贡献为0
若 k = = 1 k==1 k==1
贡献显然并不是 d p [ i − 1 ] [ 1 ] dp[i-1][1] dp[i−1][1]
首先1~j-1一定是升序排列的,否则就会存在不合法的下降子序列
若不合法,则存在 x < y , j > p [ x ] > p [ y ] > 1 x<y,j>p[x]>p[y]>1 x<y,j>p[x]>p[y]>1
考虑一个映射:
把x移到x+1的位置上,特别的让j-1移到1的位置上
则新序列和原序列是一一对应的,即是一个双双射
且新数列中存在 ( j − 1 , p [ x ] − 1 , p [ y ] − 1 ) (j-1,p[x]-1,p[y]-1) (j−1,p[x]−1,p[y]−1)的下降子序列
所以若填的k等于1,则等价于不存在以j-1开始的长度为3的下降子序列,即dp[i-1][j-1]
综合三种情况
d p [ i ] [ j ] = ∑ k = j − 1 i − 1 d p [ i − 1 ] [ k ] dp[i][j]=\sum_{k=j-1}^{i-1}dp[i-1][k] dp[i][j]=∑k=j−1i−1dp[i−1][k]
考虑字典序的限制
枚举与给定排列的最长公共前缀i
设前缀最大值为x,满足 p [ j ] < = x , j > = i p[j]<=x,j>=i p[j]<=x,j>=i的j的个数为s
那么离散化之后,x就是s
又因为有字典序的限制,所以填的数字应该>p[i],也就是说,当前位置可以填的最小的数是s+1,最大的是n-i+1,当然是离散化之后的
即答案是
A n s = ∑ i = 1 n − 1 ∑ k = s + 1 n − i + 1 d p [ n − i + 1 ] [ k ] Ans=\sum_{i=1}^{n-1}\sum_{k=s+1}^{n-i+1}dp[n-i+1][k] Ans=i=1∑n−1k=s+1∑n−i+1dp[n−i+1][k]
预处理dp值,通过前缀和优化,复杂度 O ( n 2 ) O(n^2) O(n2)
因为代码很简单,所以略
100pts
观察到答案实际上若干dp数组的后缀和
设 s ( n , m ) = ∑ i = m n d p [ n ] [ i ] s(n,m)=\sum_{i=m}^ndp[n][i] s(n,m)=∑i=mndp[n][i],其中 m ≤ n m\leq n m≤n
则
s ( n , m ) = ∑ i = m + 1 n d p [ n ] [ i ] + d p [ n ] [ m ] s(n,m)=\sum_{i=m+1}^ndp[n][i]+dp[n][m] s(n,m)=i=m+1∑ndp[n][i]+dp[n][m]
= ∑ i = m + 1 n d p [ n ] [ i ] + ∑ i = m − 1 n − 1 d p [ n − 1 ] [ i ] =\sum_{i=m+1}^ndp[n][i]+\sum_{i=m-1}^{n-1}dp[n-1][i] =i=m+1∑ndp[n][i]+i=m−1∑n−1dp[n−1][i]
= s ( n , m + 1 ) + s ( n − 1 , m − 1 ) =s(n,m+1)+s(n-1,m-1) =s(n,m+1)+s(n−1,m−1)
考虑组合意义,
在平面直角坐标系上从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , m ) (n,m) (n,m)每次向下走一步或者向右上走一步的方案数
并且不能超过 y = x y=x y=x这条线和x轴之下
首先仅通过这两种走法,随便走,不可能越过 y = x y=x y=x,但有可能越过x轴
因为只有第二种会向右走一步,所以一共有n次第二种操作,也可以得到有n-m次第一种操作
为了让这两种操作相似,不妨把想下,改为向右
则原先走到(n,m),新的操作就会走到(2n-m,m),因为多向右走了n-m次
即,每次向右上或者右下走一步,走到(2n-m,m)的方案数,且不能超过x轴
这和原问题也是双射
因为不能超过x轴,所以每个前缀里,向上走的次数要大于等于向下走的次数
这就是卡特兰数的变种问题,方案数为 s ( n , m ) = C 2 n − m n − m − C 2 n − m n − m − 1 s(n,m)=C_{2n-m}^{n-m}-C_{2n-m}^{n-m-1} s(n,m)=C2n−mn−m−C2n−mn−m−1
A n s = ∑ i = 1 n − 1 s ( n − i + 1 , s + 1 ) Ans=\sum_{i=1}^{n-1}s(n-i+1,s+1) Ans=i=1∑n−1s(n−i+1,s+1)
预处理组合数,用树状数组求s
复杂度 O ( n log n ) O(n\log n) O(nlogn)
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5+7;
typedef long long LL;
const int mod = 998244353;
int c[N];
int n;
void add(int x,int v)
{
for(int i=x;i<=n;i+=i&-i)
c[i]+=v;
}
int ask(int x)
{
int res=0;
for(int i=x;i;i-=i&-i)
res+=c[i];
return res;
}
int a[N];
LL fac[N*2],inv[N*2];
LL M = N*2-2;
LL Pow(LL a,LL b)
{
LL res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int pre[N],suf[N];
int leq[N];
LL C(int n,int m)
{
if(n<m) return 0;
return (LL)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
LL S(int n,int m)
{
return (C(2*n-m,n-m)-C(2*n-m,n-m-1)+mod)%mod;
}
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
pre[i]=max(pre[i-1],a[i]);
suf[n+1]=n+1;
for(int i=n;i>=1;i--)
suf[i]=min(suf[i+1],a[i]);
for(int i=n;i>=1;i--)
{
add(a[i],1);
leq[i]=ask(pre[i]);
}
for(int i=1;i<=n;i++)
add(a[i],-1);
int Max=0;
LL ans=0;
for(int i=1;i<=n;i++)
{
if(Max>suf[i]) break;
if(leq[i]+1<=n-i+1)
ans=(ans+S(n-i+1,leq[i]+1))%mod;
if(a[i]<pre[i]) Max=max(Max,a[i]);
}
printf("%lld\n",ans);
}
int main()
{
int T;
cin>>T;
fac[0]=1;
for(int i=1;i<=M;i++)
fac[i]=(LL)fac[i-1]*i%mod;
inv[M]=Pow(fac[M],mod-2);
for(int i=M-1;i>=0;i--)
inv[i]=(LL)inv[i+1]*(i+1)%mod;
while(T--)
{
solve();
}
return 0;
}
边栏推荐
猜你喜欢

Two dimensional code generation based on MCU and two dimensional code display on ink screen

Iptables learning

Analysis on data collection and analysis of network security competition in national vocational college skill competition

时间继电器

Redis learning

中职磐云网络安全大赛-------隐藏信息探索

Leetcode15 -- sum of three numbers

51单片机内部外设:实时时钟(SPI)

Hill sort of seven sorts

Markdown extended syntax
随机推荐
格力口罩来了!KN95口罩只要5.5元一个!
OPPO Find X2系列发布:3K+120Hz曲面屏,DXO评分第一,顶配版6999元!
Starfish Os X MetaBell战略合作,元宇宙商业生态更进一步
Tab bar (addeventlistener and onclick practice, used with bind method, exponential growth to listen for events)
QT common operation collection
Starfish OS X metabell strategic cooperation, metauniverse business ecosystem further
It is said that Huawei will cut the order again! Supply chain manufacturers are more difficult
In depth understanding of redis master-slave principle
只会Excel想做图表可视化,让数据动起来?可以,快来围观啦(附大量模板下载)
Leetcode383 ransom letter
[illustration] shake hands three times and wave hands four times - it's enough to read this article carefully
中职磐云网络安全大赛-------隐藏信息探索
Hc32f4a0 clock control
Are Transformers Effective for Time Series Forecasting?| Pit filling
Bluetooth framework summary
Can uniswap integrate sudoswap to open a new prelude to NFT liquidity?
Time relay
EC code introduction
Chapter 3 business function development (choose to export market activities, Apache POI)
ADI、世健、骏龙科技共同捐赠230万元助力湖北抗疫