专栏文章
题解:P10813 【MX-S2-T4】 换
P10813题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minn7wqc
- 此快照首次捕获于
- 2025/12/02 05:10 3 个月前
- 此快照最后确认于
- 2025/12/02 05:10 3 个月前
2025/11/12 upd:修改笔误。
好题,融入了一些自己的思路历程,可能看得会更舒服一点。
分析题目所给的操作,很难发现什么有用的性质,交换路径与 序列密切相关,如果不清楚交换路径,根本无法继续分析 单调不降的要求。
然后发现 , 的范围更像是状压的复杂度,允许 枚举, 显得有些神秘,但是计算一下 ,刚好跑得了,猜测是正解复杂度的一部分。
很大,到了 级别,说明它不能作为一个系数乘到复杂度里面,但是又和 元素的值域相关,而题目的操作只关心 的相对大小,猜测是有点钦定相对大小,用组合数分配的味道, 很小,计算形如 的组合式可以 算。
有很强烈的指示性,但现在似乎还没有任何有关 的东西。
接下来的一步比较有跳跃性,考虑换一种判定一个 合法的方式,枚举 ,将 的换成 ,反之换为 ,如果对于每个 ,其生成的 01 序列都合法,那么就合法。
如果想到了这个,那么离做出这题就不远了。
可能的 01 序列一共有 中,可以预处理 ,表示 是否是合法的 01 序列,时间复杂度 。
继续考虑判定的过程,发现 是有点宽松的,有些 的连续段的作用效果本质一样(生成了相同的 01 序列),其实只需要离散一下,用 来判定即可。
考虑生成一个合法 序列的过程,初始时每个位置都是空的,显然合法,考虑从小到大填数,假设当前填的是 ,每次选若干个位置填成 ,相应的 01 序列也会发生有规律的变化(那几个位置都变为 ),如果生成的过程中所有历经的 01 序列都是合法的,那么就能生成一个合法的 序列。
那么就可以考虑设计出一个 dp 转移来计数。
设 表示从大到小插入了 种数(只关心相对大小),当前位置的选定情况(同时也是 01 序列的情况,因为新填入的位置在 01 序列上一定为 )为 时的方案数,有转移:
这个 dp 转移是离线的,直接跑 次高位前缀和就行了,这里使用 fwt 实现。
那么答案就是 ,其中 是全集,代表 的所有位置均已填数,生成了完整的 ,原 dp 求出的是离散的方案数,还需要结合值域 和种类数 ,用组合数求出真正的方案数。
时间复杂度 ,跑得还怪快的。
CPP#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 19
#define S (1<<18)+5
#define M 505
#define int long long
const int mod=1e9+7;
int n,v,m,p[M],q[M],a[N],f[N][S];
int g[S],tmp[S],tot;
inline int qpow(int a,int b=mod-2){
int ans=1;
while(b){
if(b&1){
ans=ans*a%mod;
}
a=a*a%mod;
b>>=1;
}
return ans;
}
inline int C(int n,int m){
if(n<m){
return 0;
}
int ans=1,fac=1;
rep(i,1,m){
ans=ans*(n-i+1)%mod;
fac=fac*i%mod;
}
return ans*qpow(fac)%mod;
}
inline void init(){
tot=(1<<n)-1;
rep(s,0,tot){
rep(i,1,n){
a[i]=(s>>(i-1))&1;
}
rep(i,1,m){
if(a[p[i]]>a[q[i]]){
swap(a[p[i]],a[q[i]]);
}
}
g[s]=is_sorted(a+1,a+1+n);
}
}
inline void fwt(int *f,int n){
int len=2,nx=1;
while(len<=n){
int i=0;
while(i<n){
rep(j,0,nx-1){
f[i+j+nx]=(f[i+j+nx]+f[i+j])%mod;
}
i+=len;
}
len<<=1;
nx<<=1;
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>v>>m;
rep(i,1,m){
cin>>p[i]>>q[i];
}
init();
f[0][0]=g[0];
rep(i,1,n){
rep(s,0,tot){
tmp[s]=f[i-1][s];
}
fwt(tmp,1<<n);
rep(s,0,tot){
f[i][s]=(tmp[s]-f[i-1][s]+mod)%mod*g[s];
}
}
int ans=0;
rep(i,1,n){
ans=(ans+C(v,i)*f[i][tot]%mod)%mod;
}
cout<<ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...