zoukankan      html  css  js  c++  java
  • 【洛谷P3810】【模板】三维偏序(陌上花开)

    题目

    题目链接:https://www.luogu.com.cn/problem/P3810
    \(n\) 个元素,第 \(i\) 个元素有 \(a_i,b_i,c_i\) 三个属性,设 \(f(i)\) 表示满足 \(a_j \leq a_i\)\(b_j \leq b_i\)\(c_j \leq c_i\)\(j \ne i\)\(j\) 的数量。
    对于 \(d \in [0, n)\),求 \(f(i) = d\) 的数量。

    思路

    菜鸡\(OIer\)至今不知道\(cdq\)分治除了能应用在偏序上还能应用在哪里\(qwq\),有没有\(dalao\)可以在评论区给出几道不是偏序的\(cdq\)例题啊,谢谢\(qwq\)

    一维偏序就是排序,二维偏序就是排序\(+bit\),三维偏序就是排序\(+cdq+bit\)
    先将第一维排序,然后第二维采用分治。
    假设现在处理到的分治区间是\([l,r]\),设\(mid=\frac{l+r}{2}\),假设\([l,mid]\)\([mid+1,r]\)都已经计算完贡献。那么现在我们需要处理的就是\([l,mid]\)\([mid+1,r]\)的贡献。
    将两个区间分别按照第二位排序,因为最开始已经将第一维排过序,所以现在已经能保证\([l,mid]\)里的任意元素的第一维不大于\([mid+1,r]\)里任意元素的第一维。
    维护两个指针\(i,j\),分别扫描两个区间。当\(j\)往后扫一位时,\(i\)就不断往后扫直到元素\(i\)的第二位大于元素\(j\)的第二维。扫描同时将\([l,i)\)里所有元素的第三维扔进一个\(bit\)里,当\(i\)的第二维大于\(j\)的第二维时,\(i\)停止往后扫,同时\(j\)的答案加上\(bit.ask(j \texttt{的第三维})\)
    这样的话在最开始的排序时保证了\(i\)第一维不超过\(j\)第一维,在扫描的时候保证了\(i\)第二维不超过\(j\)第二维,在查询前缀和时只能查询到第三维不超过\(j\)的第三维的\(i\)的个数。
    这样三维偏序就完成了,时间复杂度\(O(n\log^2 n)\)或者说\(O(n\log n\log k)\),笔者不太严谨。

    需要注意的是这道题会存在两个元素的三维全都相同,但此时两者是互相能产生贡献的,所以我们将多个完全相同的元素在最开始就合并,每次\(bit.add\)时变成这个元素的个数,同时这个元素的答案一开始就要加上\((\)这个元素个数\(-1)\)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=200010;
    int n,m,k,ans[N];
    
    struct node
    {
    	int a,b,c,cnt,num;
    }a[N],b[N];
    
    bool cmp1(node x,node y)
    {
    	if (x.a<y.a) return 1;
    	if (x.a>y.a) return 0;
    	if (x.b<y.b) return 1;
    	if (x.b>y.b) return 0;
    	return x.c<y.c;
    }
    
    bool cmp2(node x,node y)
    {
    	return x.b<y.b;
    }
    
    struct BIT
    {
    	int c[N];
    	
    	void add(int x,int val)
    	{
    		if (!x) return;
    		for (;x<=k;x+=x&-x)
    			c[x]+=val;
    	}
    	
    	int ask(int x)
    	{
    		int ans=0;
    		for (;x;x-=x&-x)
    			ans+=c[x];
    		return ans;
    	}
    }bit;
    
    void cdq(int l,int r)
    {
    	if (l==r) return;
    	int mid=(l+r)>>1,i=l;
    	cdq(l,mid); sort(a+l,a+mid+1,cmp2);
    	cdq(mid+1,r); sort(a+mid+1,a+r+1,cmp2);
    	for (int j=mid+1;j<=r;j++)
    	{
    		for (;a[i].b<=a[j].b && i<=mid;i++)
    			bit.add(a[i].c,a[i].num);
    		a[j].cnt+=bit.ask(a[j].c);
    	}
    	for (int j=l;j<i;j++)
    		bit.add(a[j].c,-a[j].num);
    }
    
    int main()
    {
    	scanf("%d%d",&m,&k);
    	for (int i=1;i<=m;i++)
    		scanf("%d%d%d",&b[i].a,&b[i].b,&b[i].c);
    	sort(b+1,b+1+m,cmp1);
    	for (int i=1;i<=m;i++)
    	{
    		int last=i;
    		while (b[i].a==b[i+1].a && b[i].b==b[i+1].b && b[i].c==b[i+1].c) i++;
    		a[++n]=b[last]; a[n].num=i-last+1; a[n].cnt=a[n].num-1;
    	}
    	cdq(1,n);
    	for (int i=1;i<=n;i++)
    		ans[a[i].cnt]+=a[i].num;
    	for (int i=0;i<m;i++)
    		printf("%d\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Android 判断app何时是打开或者关闭的技术研究
    JSON返回DateTime/Date('123123123')/解决办法
    SpringMVC日期类型转换问题三大处理方法归纳
    jackson 转json. 过滤null值
    将java.util.Date类型转换成json时,使用JsonValueProcessor将date转换成希望的类型
    jackson 实体转json 为NULL或者为空不参加序列化
    美工与程序猿的Web工作怎样做到相对分离?
    jQuery 序列化表单数据 serialize() serializeArray()
    iOS开发- 隐藏状态栏(电池栏)
    《Programming Hive》读书笔记(一)Hadoop和hive环境搭建
  • 原文地址:https://www.cnblogs.com/stoorz/p/12222206.html
Copyright © 2011-2022 走看看