【CF1548E】Gregor and the Two Painters

题目

题目链接: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)) 是一个连通块内最小的格子,必然有:

  1. (a_i+b_jleq X)
  2. (c_i+b_j>X)
  3. (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;
}
原文地址:https://www.cnblogs.com/stoorz/p/15094041.html