专栏文章
题解:AT_agc012_f [AGC012F] Prefix Median
AT_agc012_f题解参与者 5已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @mipevxqz
- 此快照首次捕获于
- 2025/12/03 10:52 3 个月前
- 此快照最后确认于
- 2025/12/03 10:52 3 个月前
提供一个官方题解的证明方式(图也源自官方题解)。
dp 本来想也用官方题解的,不过发现 WTimeLlimit的方法和官方题解本质一样,且更加优美,遂学习了这位大佬的。
寻找一个 合法的充要条件,对其计数。
先假设 为一个 的排列。
必要条件比较好找:
- ,因为 必定右 个大于和小于他的数。
- 不存在 ,满足 ,因为每次我们的中位数顶多移动一位。
证明其充分性,即我们按照上面的限制可以构造一个序列。
考虑数学归纳法:对于任意正整数 ,当给他长度为 的排列 时,可以构造出一个满足上升要求的 。
当 的时候显然。
设 时成立,证明 的时候成立。此时必定有 。
而我们可以通过证明对于一个 的满足上述限制的 ,可以通过删除 中的两个数,重标号后,达到一个 的满足上述限制的 ,反之可以证明可以由 推广到 。
而我们只需要证明删除两个数之后,,分讨一下:
-
当 时:如下图所示,分为 和 两个组。从后一个组中去除不属于 且最接近 的两个值。由于后者的组大小为 ,因此一定可以去除。此时也一定不会破坏约束条件 2 和 3。的情况也大致相同。

-
当 时:如下图所示,分为 和 两个组。从两个组中分别去除一个不属于 且最接近 的一个值。由于两个组的大小均为 ,因此一定可以去除。此时也一定不会破坏约束条件 2 和 3。

综上,得证。
那么,考虑如何求解呢?
根据上述条件,可以知道 的候选值数量比 的多两个,且从后往前选择的时候, 之间的值不能作为候选值,这启发我们从后往前 dp。
设 表示考虑了 ,在 内,有 个合法的候选值 , 个合法的候选值 。
转移有:
- ,即 的情况。
- ,要求满足 ,即 的情况,且在这两个之间的候选值都要被干掉。
- ,要求满足 ,即 的情况,且在这两个之间的候选值都要被干掉。
初始值为 ,最终答案为 。
时间为 。
对于 不是排列的时候,我们先排序,然后由值域的扩展了确定是否有那个 。
C#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define p_b push_back
#define m_p make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define gcd __gcd
#define lowbit(x) (x&(-x))
using namespace std;
int rd(){
int x=0,f=1; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
return x*f;
}
void write(int x){
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=50+5,INF=0x3f3f3f3f,mod=1e9+7;
void add(int &x,int y){
x+=y;
if(x>=mod)x-=mod;
}
int dp[N][N<<1][N<<1],a[N<<1],n,ans;
int main(){
n=rd();
for(int i=1;i<2*n;i++)a[i]=rd();
sort(a+1,a+2*n);
dp[n][0][0]=1;
for(int i=n;i>=2;i--){
int x=(a[i-1]!=a[i]),y=(a[2*n-(i-1)]!=a[2*n-i]);
for(int j=0;j<=2*n;j++){
for(int k=0;k<=2*n;k++){
if(!dp[i][j][k])continue;
add(dp[i-1][j+x][k+y],dp[i][j][k]);
for(int _j=0;_j<j+x;_j++)add(dp[i-1][_j][k+1+y],dp[i][j][k]);
for(int _k=0;_k<k+y;_k++)add(dp[i-1][j+1+x][_k],dp[i][j][k]);
}
}
}
for(int j=0;j<=2*n;j++)for(int k=0;k<=2*n;k++)add(ans,dp[1][j][k]);
printf("%d\n",ans);
return 0;
}
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...