【CF1548E】Gregor and the Two Painters

题目

题目链接:https://codeforces.com/contest/1548/problem/E
一个 \(n\times m\) 的网格,其中 \((i,j)\) 的权值为 \(a_i+b_j\)。
把所有权值不超过 \(X\) 的格子全部染成白色,大于 \(X\) 的染成黑色,求最后白色连通块的数量。
\(n,m,X,a_i,b_i\leq 2\times 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_j\leq X\),\(j'\in[l2,r2]\) 都满足 \(a_i+b_{j'}\leq X\),那么一定有 \(a_i=\min_{k\in[l1,r1]}a_k\),\(b_j=\min_{k\in[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_{j\in(pre_i,i]}a_j,\max_{j\in[i,nxt_i)}a_j)\)。同理 \(d\) 就是在 \(b\) 上搞这个玩意,那么若一个格子 \((i,j)\) 是一个连通块内最小的格子,必然有:

  1. \(a_i+b_j\leq 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_i\leq d_j-b_j\) 的二元组数量。
而 \(c_i-a_i\leq d_j-b_j\) 等价于 \(c_i+b_j\leq a_i+d_j\),而条件 \(2\) 中已经满足 \(c_i+b_j>X\),所以也就必然有 \(c_i+b_j> X\)。而对于 \(X<c_i+b_j\leq a_i+d_j\) 的部分,则会在形如 \((b_i,d_i)\) 的二元组中计算。
这样所有情况很巧妙的恰好被计算了一次。
再在 \(a\) 的树状数组中下标 \(a_i\) 加 \(1\) 即可。
时间复杂度 \(O(n\log n+m\log 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;
}
上一篇:top工具的安装-(shell4)


下一篇:if (()) [[]] [] 表达式比较示例