• 【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;
    }
    
  • 相关阅读:
    C#之Attribute(特性)
    MongoDB 学习笔记
    WCF学习笔记(基于REST规则方式)
    一个简单的WebService实例
    简单的NHibernate学习笔记
    ASP.NET MVC 搭建简单网站 --1.前端页面布局和基本样式实现
    sql触发器知识
    Aspose.Cells.dll引用导入导出Excel
    Sql语句批量更新数据(多表关联)
    JsTree异步加载数据实现多级菜单
  • 原文地址:https://www.cnblogs.com/stoorz/p/15094041.html
Copyright © 2020-2023  润新知