题目
题目链接:https://codeforces.com/contest/1548/problem/E
一个 (n imes m) 的网格,其中 ((i,j)) 的权值为 (a_i+b_j)。
把所有权值不超过 (X) 的格子全部染成白色,大于 (X) 的染成黑色,求最后白色连通块的数量。
(n,m,X,a_i,b_ileq 2 imes 10^5)。
思路
基本上就是 CF 官方题解抄过来的(
如果存在 (i,j(i<j)) 满足 (a_i=a_j),那么我们就当做 (a_j>a_i)。(b) 同理。
这样的话,对于每一个连通块,就必然有恰好一个格子的权值是最小的。首先每一个连通块必然至少有一个格子的权值最小,然后考虑这个权值最小的格子 ((i,j))。假设 (i'in[l1,r1]) 都满足 (a_{i'}+b_jleq X),(j'in[l2,r2]) 都满足 (a_i+b_{j'}leq X),那么一定有 (a_i=min_{kin[l1,r1]}a_k),(b_j=min_{kin[l2,r2]}b_k)。且该连通块一定在 ((l1,l2),(r1,r2)) 这个矩形内。所以每一个连通块也就可以恰好找到一个最小值。
那么我们只需要找有多少个这样的格子就行了。记 (pre_i) 为最大的 (j(j<i)) 满足 (a_j<a_i),(nxt_i) 为最小的 (j(j>i)) 满足 (a_j<a_i);记 (c_i=min(max_{jin(pre_i,i]}a_j,max_{jin[i,nxt_i)}a_j))。同理 (d) 就是在 (b) 上搞这个玩意,那么若一个格子 ((i,j)) 是一个连通块内最小的格子,必然有:
- (a_i+b_jleq X)
- (c_i+b_j>X)
- (a_i+d_j>X)
也就是 ((i,j)) 的权值不超过 (X),且第 (i) 行往上往下找到第一个小于 (a_i) 的那一行中间必然有一行连不过去;且第 (j) 列往左往右找第一个小于 (b_j) 的那一列中间必然有一列连不过去。
可以通过 ST 表 + 单调栈来求出 (c,d),那么接下来就是需要统计有多少 ((i,j)) 满足上述条件。
将所有二元组 ((a_i,c_i),(b_i,d_i)) 扔进一个数组里,把所有二元组 ((x,y)) 按照 (y-x) 从大到小排序。
维护 (a,b) 两个树状数组,然后对于一个二元组,假设它是 ((a_i,c_i)) 的形式,那么就在 (b) 的树状数组内查询 ((X-c_i,X-a_i]) 的和。这样就统计了同时满足条件 (1,2),且满足 (c_i-a_ileq d_j-b_j) 的二元组数量。
而 (c_i-a_ileq d_j-b_j) 等价于 (c_i+b_jleq a_i+d_j),而条件 (2) 中已经满足 (c_i+b_j>X),所以也就必然有 (c_i+b_j> X)。而对于 (X<c_i+b_jleq a_i+d_j) 的部分,则会在形如 ((b_i,d_i)) 的二元组中计算。
这样所有情况很巧妙的恰好被计算了一次。
再在 (a) 的树状数组中下标 (a_i) 加 (1) 即可。
时间复杂度 (O(nlog n+mlog m))。
代码
#include <bits/stdc++.h>
#define mp(x,y,z) make_pair(x,make_pair(y,z))
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int N=200010,LG=18,Inf=1e9;
int n,m,k,a[N],b[N],c[N],d[N],lg[N],st[N][LG+1];
ll ans;
pair<int,pair<int,int> > qry[N*2];
stack<int> s;
void buildst(int n,int *a)
{
for (int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for (int i=1;i<=n;i++) st[i][0]=a[i];
for (int i=n;i>=1;i--)
for (int j=1;i+(1<<j)-1<=n;j++)
st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
int query(int l,int r)
{
int k=lg[r-l+1];
return max(st[l][k],st[r-(1<<k)+1][k]);
}
void work(int n,int *a,int *c)
{
for (int i=1;i<=n;i++)
{
c[i]=Inf;
while (s.size() && a[s.top()]>=a[i]) s.pop();
if (s.size()) c[i]=query(s.top()+1,i);
s.push(i);
}
while (s.size()) s.pop();
for (int i=n;i>=1;i--)
{
while (s.size() && a[s.top()]>a[i]) s.pop();
if (s.size()) c[i]=min(c[i],query(i,s.top()-1));
s.push(i);
}
while (s.size()) s.pop();
}
struct BIT
{
int c[N];
void add(int x,int v)
{
for (int i=x;i<N;i+=i&-i)
c[i]+=v;
}
int query(int x)
{
int ans=0;
for (int i=x;i>0;i-=i&-i)
ans+=c[i];
return ans;
}
}bit[2];
bool cmp(pair<int,pair<int,int> > x,pair<int,pair<int,int> > y)
{
return x.se.fi-x.fi>y.se.fi-y.fi;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=m;i++) scanf("%d",&b[i]);
buildst(n,a); work(n,a,c);
buildst(m,b); work(m,b,d);
for (int i=1;i<=n;i++) qry[i]=mp(a[i],c[i],0);
for (int i=1;i<=m;i++) qry[i+n]=mp(b[i],d[i],1);
sort(qry+1,qry+1+n+m,cmp);
for (int i=1;i<=n+m;i++)
{
int x=qry[i].fi,y=qry[i].se.fi,typ=qry[i].se.se;
if (x<=y) ans+=bit[typ^1].query(k-x)-bit[typ^1].query(k-y);
bit[typ].add(x,1);
}
cout<<ans;
return 0;
}