题解:[BalticOI 2024] Wall
题意分析
对于 \(2^n\) 种序列 \(h\),都有 \(h_i=a_i\) 或 \(h_i=b_i\)。先考虑对于确定的 \(h\),如何计算答案。
显然可以对 \(a,b\) 离散化,记离散化后值域为 \([1,m]\),\(m\leq 2n\)。下文中不再区分离散化前/后的值。
\(h_i\) 的水位线 \(H_i\) 需要满足 \(\exist l\leq i\leq r,H_i\leq\min(h_l,h_r)\) 且 \(H_i\) 最大,即 \(H_i=\min(\textit{pre}_i,\textit{suf}_i)\)。\(\textit{pre}_i,\textit{suf}_i\) 分别为前缀/后缀最大值。
于是有序列 \(h\) 的贡献为:
前/后缀 \(\max\) 取 \(\min\),不好计算,考虑转换一下。经典结论 \(\min(a,b)=a+b-\max(a,b)\),而 \(\max(\textit{pre}_i,\textit{suf}_i)\) 就是全局 \(\max\),记为 \(f\):
\(\mathcal O(2^n)\) 枚举肯定是不行的,考虑全局统计贡献。
先考虑统计 \(\displaystyle\sum_{i=1}^nh_i\),这是好统计的。因为 \(h_i=a_i\) 或 \(h_i=b_i\) 都只有 \(2^{n-1}\) 种情况,这一部分的总贡献即:
再考虑 \(nf\) 怎么做。考虑 \(f\) 只有 \(m\) 种取值,枚举 \(f\) 的取值,随后计算最大值为 \(f\) 的方案数。记 \(k(x)\) 表示最大值小于等于 \(x\) 的方案数量。
考虑 \(a_i,b_i\) 对于方案的贡献:
- 如果 \(\max(a_i,b_i)\leq x\),则方案数为 \(2\)。
- 如果 \(\min(a_i,b_i)\leq x<\max(a_i,b_i)\),则方案数为 \(1\)。
- 如果 \(x<\min(a_i,b_i)\),则方案数为 \(0\)。
总方案数即每一对 \((a_i,b_i)\) 的方案数的之积。暴力 \(\mathcal O(n)\) 计算是不能接受的,考虑优化。记 \(\displaystyle L=\max_{i=1}^n\min(a_i,b_i)\),则 \(x<L\) 时 \(k(x)=0\)。记 \(c_i\) 表示 \(\max(a_j,b_j)\leq i\) 的个数,前缀和即可 \(\mathcal O(n)\) 预处理,有 \(\displaystyle k(x)=[L\leq x]2^{c_x}\)。
预处理一下 \(2\) 的幂,即可 \(\mathcal O(n)\) 统计。这部分的贡献为:
显然求 \(\textit{suf}_i\) 的贡献可以把序列翻转后再求一遍,只考虑统计 \(\textit{pre}_i\) 的贡献。
考虑枚举 \(\textit{pre}_i=j\),计算方案数。后面的 \(n-i\) 个数随便选,\(2^{n-i}\) 种情况。
设 \(d_j\) 表示前缀最大值小于等于 \(j\) 的方案数。则加入 \((a_i,b_i)\) 时,有转移:
于是可以 \(\mathcal O(n^2)\) 计算贡献,期望得分 70pts。
这个东西看起来不太能继续推导,考虑数据结构优化。
枚举加入 \((a_i,b_i)\) 是不能省略的,但是可以发现,修改操作其实是两个区间 \([0,\min(a_i,b_i)-1],[\max(a_i,b_i),m]\) 分别乘 \(0,2\)。启发我们使用线段树维护。
但是 \(\textit{pre}_i=j\) 的贡献形如 \(j(d_j-d_{j-1})\),直接用线段树维护 \(d\) 的话不好统计答案。因为 \(d_j\) 对于答案的贡献还涉及到 \(d_{j-1}\),区间边界的地方比较难做。考虑拆贡献,有 \(j\) 的贡献为 \(j(d_j-d_{j-1})=j\cdot d_j-(j+1)\cdot d_j\)。
需要注意的是,这里不能直接合并,不仅仅因为离散化,还因为统计答案的时候需要把加法贡献、减法贡献分开统计:因为我们实际上是把 \(j\) 的 \(-j\cdot d_{j-1}\) 的贡献转移到了 \(j-1\) 上维护,从而便于维护 \(d_j\)。
此时,\(i\) 的贡献即乘完之后 \([\min(a_i,b_i),m]\) 的加法标记和减去 \([\min(a_i,b_i)-1,m-1]\) 的减法标记和。
于是可以 \(\mathcal O(n\log n)\) 维护。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=5e5,P=1e9+7;
int n,a[N+1],b[N+1],pow2[N<<1|1];
int m,tmp[N*2+1+1];
void discrete(){
m=0;
for(int i=1;i<=n;i++){
tmp[++m]=a[i];
tmp[++m]=b[i];
}
sort(tmp+1,tmp+m+1);
m=unique(tmp+1,tmp+m+1)-tmp-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(tmp+1,tmp+m+1,a[i])-tmp;
b[i]=lower_bound(tmp+1,tmp+m+1,b[i])-tmp;
}
}
struct segTree{
struct node{
int l,r;
int add,minus,c,tag;
}t[N<<3|1];
void up(int p){
t[p].add=(t[p<<1].add+t[p<<1|1].add)%P;
t[p].minus=(t[p<<1].minus+t[p<<1|1].minus)%P;
}
void build(int p,int l,int r){
t[p]={l,r};
t[p].tag=1;
if(l==r){
t[p].c=(l>0);
t[p].add=1ll*tmp[l]*t[p].c%P;
t[p].minus=1ll*tmp[l+1]*t[p].c%P;
return;
}
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
up(p);
}
void down(int p){
if(t[p].tag!=1){
t[p<<1].add=1ll*t[p<<1].add*t[p].tag%P;
t[p<<1].minus=1ll*t[p<<1].minus*t[p].tag%P;
t[p<<1].c=1ll*t[p<<1].c*t[p].tag%P;
t[p<<1].tag=1ll*t[p<<1].tag*t[p].tag%P;
t[p<<1|1].add=1ll*t[p<<1|1].add*t[p].tag%P;
t[p<<1|1].minus=1ll*t[p<<1|1].minus*t[p].tag%P;
t[p<<1|1].c=1ll*t[p<<1|1].c*t[p].tag%P;
t[p<<1|1].tag=1ll*t[p<<1|1].tag*t[p].tag%P;
t[p].tag=1;
}
}
void mul(int p,int l,int r,int x){
if(l<=t[p].l&&t[p].r<=r){
t[p].add=1ll*t[p].add*x%P;
t[p].minus=1ll*t[p].minus*x%P;
t[p].c=1ll*t[p].c*x%P;
t[p].tag=1ll*t[p].tag*x%P;
return;
}
down(p);
if(l<=t[p<<1].r){
mul(p<<1,l,r,x);
}
if(t[p<<1|1].l<=r){
mul(p<<1|1,l,r,x);
}
up(p);
}
int queryAdd(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].add;
}
int ans=0;
down(p);
if(l<=t[p<<1].r){
ans+=queryAdd(p<<1,l,r);
}
if(t[p<<1|1].l<=r){
ans+=queryAdd(p<<1|1,l,r);
}
ans%=P;
return ans;
}
int queryMinus(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].minus;
}
int ans=0;
down(p);
if(l<=t[p<<1].r){
ans+=queryMinus(p<<1,l,r);
}
if(t[p<<1|1].l<=r){
ans+=queryMinus(p<<1|1,l,r);
}
ans%=P;
return ans;
}
}t;
int ans;
void calc(){
t.build(1,0,m);
for(int i=1;i<=n;i++){
t.mul(1,0,min(a[i],b[i])-1,0);
t.mul(1,max(a[i],b[i]),m,2);
ans=(ans+1ll*pow2[n-i]*(t.queryAdd(1,min(a[i],b[i]),m)-t.queryMinus(1,min(a[i],b[i])-1,m-1)))%P;
}
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
pow2[0]=1;
for(int i=1;i<=N<<1;i++){
pow2[i]=2ll*pow2[i-1]%P;
}
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
discrete();
calc();
reverse(a+1,a+n+1);
reverse(b+1,b+n+1);
calc();
reverse(a+1,a+n+1);
reverse(b+1,b+n+1);
static int c[N<<1|1];
int L=0;
for(int i=1;i<=n;i++){
L=max(L,min(a[i],b[i]));
c[max(a[i],b[i])]++;
}
for(int i=1;i<=m;i++){
c[i]+=c[i-1];
}
auto k = [&](int x){
if(x<L){
return 0;
}
return pow2[c[x]];
};
for(int i=1;i<=m;i++){
ans=(ans-1ll*tmp[i]*n%P*(k(i)-k(i-1)))%P;
}
for(int i=1;i<=n;i++){
ans=(ans-1ll*pow2[n-1]*(tmp[a[i]]+tmp[b[i]]))%P;
}
ans=(ans+P)%P;
cout<<ans<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号