2026.2.24 模拟赛
https://oj.gxyzh.com/d/hzoj/contest/6966f780c01e1c1c7ca5b72f
差值
题解
不妨先排序。
易于得到一个数组的差值和为 \(2\sum_{i=1}^n(a_i\times i)-(n+1)\sum_{i=1}^n a_i\)
对于 \(n\le 10\),使用状压暴力枚举。
对于 \(n\le 2000\),考虑数组中每一个数对上面的式子的贡献。
将贡献拆成拆成两部分:\(\sum_{i=1}^n(a_i\times i)\) 与 \(\sum_{i=1}^n a_i\)。
第一个:对于每个数枚举,钦定它的排名贡献为 \(2\sum_{i=1}^n\sum_{j=1}^ka_i\times j\times C_{i-1}^{j-1}\times C_{n-i}^{k-j}\).
第二个:直接算出每个数的计算次数 \((k+1)C_{n-1}^{k-1}\sum_{i=1}^na_i\)(钦定这个数必出现,剩下的再选)。
时间复杂度为 \(O(n^2)\)(然后我不会优化)
对于 \(n\le 20000\),直接考虑差值的贡献。
发现: \((a_i,a_j)\) 的差值产生贡献,等价于 \(a_i,a_j\) 都被选进数组。
那么每个差值产生的贡献就是 \(C_{n-2}^{k-2}\)。
所以 \(ans=C_{n-2}^{k-2}\times (2\sum_{i=1}^n(a_i\times i)-(n+1)\sum_{i=1}^n a_i)\)
时间复杂度为 \(O(nlogn)\)。
代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
constexpr int N=2e5+5,p=1e9+7;
int n,k,a[N],b[N],ans;
int fac[N],inv[N],fac_inv[N];
void init(){
fac[0]=inv[0]=fac_inv[0]=1;
fac[1]=inv[1]=fac_inv[1]=1;
for(int i=2;i<=n;i++)
fac[i]=fac[i-1]*i%p,
inv[i]=(p-p/i)*inv[p%i]%p,
fac_inv[i]=fac_inv[i-1]*inv[i]%p;
}int C(int n,int m){
if(n<m)return 0;
return fac[n]*fac_inv[m]%p*fac_inv[n-m]%p;
}signed main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>k,init();
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
ans=(ans+2*a[i]*i%p-(n+1)*a[i]%p)%p;
ans=ans*C(n-2,k-2)%p;
cout<<(ans+p)%p;
return 0;
}
跳槽
题解
对于 \(n\le 10\),爆搜即可(\(10\) 以内跑得飞快)。
对于 \(n\le 100\),转化题意;
注意到爆搜检验时用到了单调栈,再仔细思考并画图,
不难发现跳槽就相当于沿着笛卡尔树的边向上!
而 \(l[i]\) 和 \(r[i]\) 就是左右子树中最长链长度!
那么 \(|l[i]-r[i]|\le 1\) 不就是规定了一棵 \(AVL\) 树?
于是题目就是求:节点数为 \(n\) 的笛卡尔树并且还是 \(AVL\) 树的情况。
笛卡尔树的题经常考虑区间 \(dp\)。
在此题中设 \(dp_{i,j}\) 表示节点数为 \(i\),深度为 \(j\) 的情况数。
易于有 \(dp_{i,j}=\sum_{l=0}^{i-1}(dp_{l,j-1}\times dp_{i-1-l,j-1}+dp_{l,j-2}\times dp_{i-1-l,j-1}+dp_{l,j-1}\times dp_{i-1-l,j-2})\times C_{i-1}^l\)
时间复杂度为 \(O(n^3)\)。
对于 \(n\le 5000\),考虑剪枝。
注意到 \(AVL\) 树其实是一棵平衡树……
所以深度从 \(logi+1\) 开始枚举,若 \(dp_{i,j}=0\) 就跳出。
经实测,在 \(n\le 5000\) 的范围内,只需要枚举 \(4\) 次 \(j\)。
时间复杂度为 \(O(n^2)\)。
附加:虽然题目至多考虑到 \(O(n^2)\),但是秉持着抢最优解的原则,我们继续优化。
发现,最内层循环还可以进行剪枝(寻找上下界),发现实际上只需枚举 \(O(logn)\) 次。
时间复杂度为 \(O(nlogn)\)。
代码
#include<iostream>
#define int long long
using namespace std;
constexpr int N=5e3+5,p=1e9+7;
int n,dp[N][N],ans,log[N];
int fac[N],inv[N],fac_inv[N];
void init(){
fac[0]=inv[0]=fac_inv[0]=1;
fac[1]=inv[1]=fac_inv[1]=1;
for(int i=2;i<=n;i++)
fac[i]=fac[i-1]*i%p,
inv[i]=(p-p/i)*inv[p%i]%p,
fac_inv[i]=fac_inv[i-1]*inv[i]%p;
log[0]=-1;
for(int i=1;i<=n;i++)log[i]=log[i>>1]+1;
}int C(int n,int m){
return fac[n]*fac_inv[m]%p*fac_inv[n-m]%p;
}signed main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n,dp[1][1]=1,dp[0][0]=1,init();
for(int i=2;i<=n;i++){
for(int dep=log[i]+1;dep<=n;dep++){
for(int L=0,R=i-1;R>=0;L++,R--){
int cnt=0;
cnt+=dp[L][dep-1]*dp[R][dep-1]%p;
cnt+=dp[L][dep-2]*dp[R][dep-1]%p;
cnt+=dp[L][dep-1]*dp[R][dep-2]%p;
dp[i][dep]=(dp[i][dep]+cnt*C(i-1,L)%p)%p;
}if(!dp[i][dep])break;
}
}for(int i=1;i<=n;i++)ans=(ans+dp[n][i])%p;
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号