[硫化铂]守序划分问题
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[硫化铂]守序划分问题相关的知识,希望对你有一定的参考价值。
守序划分问题
题目大意
题解
Province Team Selection=PTS=PtS=硫化铂,所以就叫这段时间的模拟赛题硫化铂吧。
结论题,我竟然没有想到
首先,既然是结论题,那么有结论,如果我们的一种划分所有集合方案使得
min
i
∈
S
A
i
⩾
max
i
∉
S
A
i
\\min_i\\in SA_i\\geqslant\\max_i\\not \\in S A_i
mini∈SAi⩾maxi∈SAi,那么一定不合法。
必要性是显然的,因为这样的话属于这两个集合内的集合肯定是不能相邻的,而它们因为无论怎么放,都要求这两个集合的点存分割点,使得需要
min
i
∈
S
A
i
⩽
max
i
∉
S
A
i
\\min_i\\in SA_i\\leqslant \\max_i\\not \\in SA_i
mini∈SAi⩽maxi∈SAi。
充分性也是可以证明的,如果满足这个条件我们一定能给出一种构造方法。
我们可以先将所有的
A
A
A按
max
A
i
\\max A_i
maxAi的大小从左往右放置,这样的话
∀
i
∈
[
2
,
m
]
,
max
A
p
i
>
min
A
p
i
−
1
\\forall i\\in[2,m],\\max A_p_i>\\min A_p_i-1
∀i∈[2,m],maxApi>minApi−1是一定成立的。
现在我们的问题是可能会有
min
A
p
n
>
max
A
p
1
\\min A_p_n>\\max A_p_1
minApn>maxAp1。但显然存在
k
k
k使得
min
A
p
k
<
min
A
p
n
<
max
A
p
k
<
max
A
p
n
\\min A_p_k<\\min A_p_n<\\max A_p_k<\\max A_p_n
minApk<minApn<maxApk<maxApn,否则我们上面的条件一定不成立,我们可以将
A
p
n
A_p_n
Apn与它大小上被完全包含的集合的集合当作被割裂出去的集合。
故我们可以将
p
k
p_k
pk与
p
n
p_n
pn交换一下,使得
max
A
p
n
\\max A_p_n
maxApn变小,可以发现事实上我们可以不断重复这个过程知道我们的
max
A
p
1
\\max A_p_1
maxAp1可以接上来。
于是我们就构造除了一种合法的方案,也就证明了它的充分性。
现在我们考虑如何统计我们的方案数。
其实上面的结论相当于这
m
m
m条线段一定是联通的,也就是说,当我们建立新的左端点时,之前已经建立的左端点对应的右端点一定尚未确立下来,否则就不联通了。
我们从小到大,将一个一个数逐步加入线段。
我们定义
d
p
i
,
j
,
k
dp_i,j,k
dpi,j,k表示枚举到第
i
i
i个点,已经确立了
j
j
j条线段的左端点,其中有
k
k
k条的右端点尚未确立。
转移比较好想,枚举这个点是用来新建立线段,不做端点地加入一条线段,或者作为一条线段的右端点,就行了。只要新线段成立时左端点不是所有左端点都右端点就好。
时间复杂度 O ( n m 2 ) O\\left(nm^2\\right) O(nm2)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 505
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e5+3;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=20000;
const int n1=1000;
const int M=100000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0')if(s=='-')f=-1;s=getchar();
while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
x*=f;
template<typename _T>
void print(_T x)if(x<0)x=(~x)+1;putchar('-');if(x>9)print(x/10);putchar(x%10+'0');
int gcd(int a,int b)return !b?a:gcd(b,a%b);
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,dp[2][MAXN][MAXN],ans;
signed main()
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
read(n);read(m);dp[0][1][1]=1;int now=0,las=1;
for(int i=2;i<=n;i++)
swap(now,las);
for(int j=1;j<=min(i,m);j++)
for(int k=1;k<=j;k++)
if(k)Add(dp[now][j][k-1],1ll*k*dp[las][j][k]%mo,mo);
Add(dp[now][j+1][k+1],dp[las][j][k],mo);
Add(dp[now][j][k],1ll*k*dp[las][j][k]%mo,mo);
if(k)Add(dp[now][j+1][k],dp[las][j][k],mo);
for(int j=1;j<=min(i,m);j++)
for(int k=0;k<=j;k++)dp[las][j][k]=0;
[硫化铂]传染