[海军国际项目办公室]打拳
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[海军国际项目办公室]打拳相关的知识,希望对你有一定的参考价值。
打拳
题目描述
题解
首先
n
⩽
9
n\\leqslant 9
n⩽9很明显已经说明了,这题很有可能是状压。
如果我们的"布呗之路"想要成为冠军 (凭空出现的条件),那他就必然需要赢下
n
n
n个人,而这
n
n
n个人分别赢过的人(它们的胜利子树内的所有点)的数量分别为
2
0
,
2
1
,
2
2
,
2
3
,
.
.
.
,
2
n
−
1
2^0,2^1,2^2,2^3,...,2^{n-1}
20,21,22,23,...,2n−1。
我们可以将所有的点按大小从小到大排序,再一个一个加入状态,因为较大的一定能赢较小的能赢的点,但较小的不一定能赢较大的能赢的点,这样的话方便统计当前加入的点还有多少点能选。
我们考虑如何维护战胜的选手的实力递增的条件。
想必大家都知道
O
(
n
log
n
)
O\\left(n\\log\\,n\\right)
O(nlogn)维护递增序列的方法,我们可以记录下每个长度的最早被击败的点的位置。
显然,一个点现在加入能得到的最长上升子序列的长度就是我们之前记录的点中比它小的点的点数。
它加入后会成为该长度的最小值,所以要把原来的替换掉,原来的那个一定是它后面的第一个,当然如果它后面你没有的话就不替换了。
我们可以定义
d
p
dp
dp状态
d
p
i
,
j
,
k
dp_{i,j,k}
dpi,j,k表示我们选到第
i
i
i个人,由冠军亲手战胜的人的情况为
j
j
j,其中被记录的情况为
k
k
k的方案数。
其转移也很好转移,具体可见代码。
当然,转移时需要注意我们新加的点前面的系数,需要计算新加点的选择情况,大概就是
d
p
i
+
1
,
j
∣
2
t
,
k
′
+
=
2
(
a
i
+
1
−
s
u
m
j
−
2
2
t
−
1
)
d
p
i
,
j
,
k
dp_{i+1,j|2^t,k'}+=2\\binom{a_{i+1}-sum_{j}-2}{2^{t}-1}dp_{i,j,k}
dpi+1,j∣2t,k′+=2(2t−1ai+1−sumj−2)dpi,j,k
的形式。
最后输出
b
i
t
S
⩾
k
bit_{S}\\geqslant k
bitS⩾k的
d
p
m
,
2
n
−
1
,
S
dp_{m,2^n-1,S}
dpm,2n−1,S即可。
由于 d p i , j , k dp_{i,j,k} dpi,j,k中, k k k一定是 j j j的子集,所以我们的时间复杂度为 O ( m 3 n ) O\\left(m3^n\\right) O(m3n)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN (1<<16)+5
#define MAXM (1<<9)+5
#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;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
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){putchar('\\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL 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&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,k,P,mx,tim,fac[MAXM],inv[MAXM],f[MAXM],bit[MAXN];
int pow2[20],a[20],C[1005][1005],dp[2][MAXM][MAXM],ans;
void init(){
pow2[0]=1;for(int i=1;i<=n;i++)pow2[i]=pow2[i-1]+pow2[i-1];
for(int i=1;i<(1<<m);i++)bit[i]=bit[i>>1]+(i&1);
fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
for(int i=0;i<=pow2[n];i++){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)
C[i][j]=add(C[i-1][j-1],C[i-1][j],P);
}
for(int i=2;i<=pow2[n];i++)
fac[i]=1ll*i*fac[i-1]%P,
f[i]=1ll*(P-P/i)*f[P%i]%P,
inv[i]=1ll*f[i]*inv[i-1]%P;
}
signed main(){
freopen("punch.in","r",stdin);
freopen("punch.out","w",stdout);
read(n);read(m);read(k);read(P);dp[0][0][0]=1;init();
for(int i=1;i<=m;i++)read(a[i]);sort(a+1,a+m+1);
for(int i=1;i<=m;i++){
int now=i&1,las=now^1;
for(int j=0;j<(1<<n);j++){
int summ=0;for(int k=0;k<n;k++)if(j&(1<<k))summ+=pow2[k];
if(a[i]+1<=summ)continue;
for(int k=0;k<n;k++)if(!(j&(1<<k))){
int tt=(1<<k)-1;
for(int t=j;1;t=j&(t-1)){
if(t&(1<<k))continue;
int tmp=2ll*C[a[i]-summ-2][pow2[k]-1]%P*fac[pow2[k]]%P;
int td=lowbit((t^(tt&t))),S=(t^td)|(1<<k);
Add(dp[now][j|(1<<k)][S],1ll*tmp*dp[las][j][t]%P,P);
if(!t)break;
}
}
}
for(int j=0;j<(1<<n);j++){
for(int t=j;t;t=j&(t-1))
Add(dp[now][j][t]以上是关于[海军国际项目办公室]打拳的主要内容,如果未能解决你的问题,请参考以下文章