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

    洛谷P3810【模板】三维偏序(陌上花开)

    题目背景

    这是一道模板题

    可以使用bitset,CDQ分治,K-DTree等方式解决。

    题目描述

    有 n 个元素,第 i 个元素有 ai 、 bi 、 ci 三个属性,设 f(i) 表示满足 ajai 且 bjbi 且 cjci 的 j 的数量。

    对于 d[0,n) ,求 f(i)=d 的数量

     

    输入格式:

     

    第一行两个整数 n 、 k ,分别表示元素数量和最大属性值。

    之后 n 行,每行三个整数 ai 、 bi 、 ci ,分别表示三个属性值。

     

    输出格式:

     

    输出 n 行,第 d+1 行表示 f(i)=d 的 i 的数量。

     

    输入输出样例

    输入样例#1: 
    10 3
    3 3 3
    2 3 3
    2 3 1
    3 1 1
    3 1 2
    1 3 1
    1 1 2
    1 2 2
    1 3 2
    1 2 1
    输出样例#1: 
    3
    1
    3
    0
    1
    0
    1
    0
    0
    1

    说明

    1n100000,1k200000

    题解:

    今儿个新学了算法:cdq分治

    此题为cdq分治三维偏序的模板题。

    我们先按照第一维从小到大排序,相等的话比下一维,以此类推。

    排完序后有个注意点,序列中的元素可能存在重复,于是要去重,代码见下:

    1 for (int i=1; i<=n; i++)
    2       if (a[i].x==a[i-1].x &&
    3           a[i].y==a[i-1].y &&
    4           a[i].z==a[i-1].z) a[m].t++;
    5       else a[++m]=a[i];    

    其中 a[i].t 存的是与之相等的元素的个数(包括自己)。

    接下来就开始cdq了:

    消除了第一维的影响,我们只需按第二维从小到大归并,第三维用树状数组维护就行了。

    还有一个要点(千万别忘了): 树状数组的数组记得清空,也不需要全部memset,只需把修改过的节点清空就可以了,效率大大提高,如下:

    for (int i=l; i<=mid; i++) change(a[i].z,-a[i].t);

    就讲到这里吧,代码如下:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=100005,K=200005,inf=0x3f3f3f3f;
     4 struct node{
     5     int x,y,z,t,s;
     6     bool operator < (const node &aa) const {
     7         return x==aa.x?(y==aa.y?z<aa.z:y<aa.y):x<aa.x;
     8     }
     9 }a[N];
    10 int n,m,k,ans[N],cnt[K];
    11 inline int read()
    12 {
    13     int x=0,f=1;
    14     char ch=getchar();
    15     while (!isdigit(ch))
    16       f=(ch=='-1')?-1:1,ch=getchar();
    17     while (isdigit(ch))
    18       x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    19     return x*f;
    20 }
    21 node tmp[N];
    22 inline int lowbit(int x)
    23 {
    24     return x&(-x);
    25 }
    26 void change(int x,int w)
    27 {
    28     for (int i=x; i<=k; i+=lowbit(i))
    29       cnt[i]+=w;
    30 }
    31 inline int get(int x)
    32 {
    33     int sum=0;
    34     for (int i=x; i; i-=lowbit(i))
    35       sum+=cnt[i];
    36     return sum;
    37 }
    38 void cdq(int l,int r)
    39 {
    40     if (l==r) return;
    41     int mid=(l+r)>>1;
    42     cdq(l,mid),cdq(mid+1,r);
    43     int u=l,v=mid+1;
    44     for (int i=l; i<=r; i++)
    45       if (v>r || (a[u].y<=a[v].y && u<=mid))
    46         change(a[u].z,a[u].t),tmp[i]=a[u++];
    47       else a[v].s+=get(a[v].z),tmp[i]=a[v++];
    48     for (int i=l; i<=mid; i++) change(a[i].z,-a[i].t);
    49     for (int i=l; i<=r; i++) a[i]=tmp[i];
    50 }
    51 int main()
    52 {
    53     n=read(),k=read(),m=0,a[0].x=a[0].y=a[0].z=-inf;
    54     for (int i=1; i<=n; i++)
    55       a[i].x=read(),a[i].y=read(),a[i].z=read(),a[i].t=1;
    56     sort(a+1,a+1+n);
    57     for (int i=1; i<=n; i++)
    58       if (a[i].x==a[i-1].x &&
    59           a[i].y==a[i-1].y &&
    60           a[i].z==a[i-1].z) a[m].t++;
    61       else a[++m]=a[i];    
    62     cdq(1,m);
    63     for (int i=1; i<=m; i++) ans[a[i].t+a[i].s-1]+=a[i].t;
    64     for (int i=0; i<n; i++) printf("%d
    ",ans[i]);
    65     return 0;
    66 }
    View Code

     这里再补充点洛谷P3157动态逆序对的解法:

    首先动态操作比较麻烦,想法办转成静态的:

    我们让被删除的数排在未被删除的数的后面,删除早的数排在删除晚的数的后面,这样一来左边的右边的就不会对左边的产生影响,而左边的仍旧可以对右边产生影响,于是就OK了。

    还有一个要注意的是:按照这样排序后,以下两种情况都是符合逆序对的:

    xi<xj && yi>yj

    xi>xj && yi<yj

    要用两个树状数组分别维护。

    代码就不贴了,可参见其他dalao。

    加油加油加油!!! fighting fighting fighting !!!

  • 相关阅读:
    BGP的MA网络、自动汇总、聚合
    [转载]CISCO配置HSRP
    BGP选路十种方法总结 用实验详细介绍(副实验拓扑)
    [win技巧] windows 7的上帝模式你用过吗?【相信你没用过!如此方便的的设置】
    CISCO BGP(EBGP/IBGP)基本配置小结以及如何防止BGP路由黑洞(附实验拓扑)
    执行外部程序
    C#编写COM组件
    在SQL Server 2005上遇到了先删除已运行维护计划后,再删除代理中由其产生的作业时,提示删除失败,如何处理?
    关于存储过程编写的一点总结(转)
    WCF:调用方未由服务器进行身份验证
  • 原文地址:https://www.cnblogs.com/Frank-King/p/9159561.html
Copyright © 2011-2022 走看看