HDU - 5517 Triple(三维偏序-二维树状数组/CDQ分治)
题目链接:点击查看
题目大意:给出 n 个二元对 ( a , b ) 和 m 个三元对 ( c , d , e ),对于所有 b == e 的二元对和三元对,可以通过某种运算形成一个新的三元对 ( a , c , d ) ,现在问所有的 ( a , c , d ) 中,有多少个三元对满足,不存在另一个三元对 ( a1 , c1 , d1 ) 满足 a1 >= a && c1 >= c && d1 >= d
题目分析:首先需要分析出来,对于所有的二元对 ( a , b ) 来说,对于每个 b ,我们只需要映射一下其最大的 a 即可,因为假设存在两个二元对 ( a1 , b ) 和 ( a2 , b ) 满足 a1 > a2,其能组成的所有三元对中,( a1 , c , d ) 和 ( a2 , c , d ) 一定满足 a1 >= a2 && c >= c && d >= d,故 a2 一定没有贡献,知道了这一点后,又因为 m 最大才为 1e5,所以最后可以做出贡献的三元对最多也只有 1e5 个,我们只需要对这些三元对进行讨论即可
将这些新的三元对储存起来后, 剩下的就是一个简单的三维偏序问题了,又因为 c 和 d 的范围都非常小,所以可以用二维树状数组来解决,时间复杂度为 nlog^2n,也可以用朴素的 CDQ分治 解决,时间复杂度同为 nlog^2n,不过显然前者的常数要小很多,表现的更加优秀
稍微讲一下二者该如何解决吧,二维树状数组应该比较好理解,说是树套树,其实就是在原有的一维数组和循环的基础上,多套了一层循环罢了,维护的是 ( 0 , 0 ) ~ ( x , y ) 这个二维矩阵中点的个数,这样一来就可以将三维偏序中的第二维和第三维抽象成二维矩阵上的点表示,在第一维降序排序的基础上,对于某个点 ( x , y ) ,如果 ( x , y ) ~ ( 1000 , 1000 ) 中存在点的话,那就说明肯定存在这一个三元组的每一项都分别大于当前的这一项,故不符合条件,反之符合条件
然后就是CDQ分治,这个没什么好说的了,按照第一维降序排序,第二维放在归并排序中仍然降序,第三维用树状数组维护,分别记录每个位置的贡献即可
最后就是特判一下无解的情况,以及对于所有相同位置的点,需要压缩成一个点,不然会相互影响
代码:
二维树状数组
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;struct Node
{int a,b,c,num;Node(int a,int b,int c,int num):a(a),b(b),c(c),num(num){}bool operator==(const Node& t)const{return a==t.a&&b==t.b&&c==t.c;}
};vector<Node>node1,node2;int b[N],num[N],c[1100][1100];int lowbit(int x)
{return x&(-x);
}int add(int x,int y)//(x,y)的位置加1
{for(int i=x;i<=1000;i+=lowbit(i))for(int j=y;j<=1000;j+=lowbit(j))c[i][j]++;
}int ask(int x,int y)//返回(0,0)~(x,y)的矩阵和
{int ans=0;for(int i=x;i>0;i-=lowbit(i))for(int j=y;j>0;j-=lowbit(j))ans+=c[i][j];return ans;
}int query(int x,int y)//返回(x,y)~(1000,1000)的矩阵和
{return ask(1000,1000)-ask(x-1,1000)-ask(1000,y-1)+ask(x-1,y-1);
}void init()
{node1.clear();node2.clear();memset(b,0,sizeof(b));memset(num,0,sizeof(num));memset(c,0,sizeof(c));
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int w;cin>>w;int kase=0;while(w--){init();int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int x,y;scanf("%d%d",&x,&y);if(b[y]<x){b[y]=x;num[y]=1;}else if(b[y]==x)num[y]++;}for(int i=1;i<=m;i++){int x,y,z;scanf("%d%d%d",&x,&y,&z);if(b[z])node1.push_back(Node(b[z],x,y,num[z]));}if(node1.empty()){printf("Case #%d: 0\n",++kase);continue;}sort(node1.begin(),node1.end(),[&](Node a,Node b){if(a.a!=b.a)return a.a>b.a;if(a.b!=b.b)return a.b>b.b;return a.c>b.c;});node2.push_back(node1[0]);for(int i=1;i<node1.size();i++){if(node2.back()==node1[i])node2.back().num+=node1[i].num;elsenode2.push_back(node1[i]);}int ans=0;for(int i=0;i<node2.size();i++){if(!query(node2[i].b,node2[i].c))ans+=node2[i].num;add(node2[i].b,node2[i].c);}printf("Case #%d: %d\n",++kase,ans);}return 0;
}
CDQ分治
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;struct Node
{int a,b,c,num,id;Node():a(0),b(0),c(0){}Node(int a,int b,int c,int num):a(a),b(b),c(c),num(num){}bool operator==(const Node& t)const{return a==t.a&&b==t.b&&c==t.c;}
}t[N];vector<Node>node1,node2;int b[N],num[N],c[1100],vis[N];int lowbit(int x)
{return x&(-x);
}int add(int x,int val)
{for(int i=x;i<=1000;i+=lowbit(i))c[i]+=val;
}int ask(int x)
{int ans=0;for(int i=x;i>0;i-=lowbit(i))ans+=c[i];return ans;
}bool cmp(Node a,Node b)
{if(a.b!=b.b)return a.b>b.b;return a.c>b.c;
}void CDQ(int l,int r)
{if(l==r)return;int mid=l+r>>1;CDQ(l,mid);CDQ(mid+1,r);for(int i=l;i<=r;i++)t[i]=node2[i];sort(t+l,t+mid+1,cmp);sort(t+mid+1,t+r+1,cmp);int p=l,q=mid+1;while(p<=mid&&q<=r){if(t[p].b>=t[q].b){add(t[p].c,1);p++;}else{vis[t[q].id]+=ask(1000)-ask(t[q].c-1);q++;}}while(p<=mid){add(t[p].c,1);p++;}while(q<=r){vis[t[q].id]+=ask(1000)-ask(t[q].c-1);q++;}for(int i=l;i<=mid;i++)add(t[i].c,-1);
}void init()
{node1.clear();node2.clear();memset(b,0,sizeof(b));memset(num,0,sizeof(num));memset(c,0,sizeof(c));memset(vis,0,sizeof(vis));
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int w;cin>>w;int kase=0;while(w--){init();int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int x,y;scanf("%d%d",&x,&y);if(b[y]<x){b[y]=x;num[y]=1;}else if(b[y]==x)num[y]++;}for(int i=1;i<=m;i++){int x,y,z;scanf("%d%d%d",&x,&y,&z);if(b[z])node1.push_back(Node(b[z],x,y,num[z]));}if(node1.empty()){printf("Case #%d: 0\n",++kase);continue;}sort(node1.begin(),node1.end(),[&](Node a,Node b){if(a.a!=b.a)return a.a>b.a;if(a.b!=b.b)return a.b>b.b;return a.c>b.c;});node2.push_back(Node());for(int i=0;i<node1.size();i++){if(node2.back()==node1[i])node2.back().num+=node1[i].num;else{node2.push_back(node1[i]);node2.back().id=node2.size()-1;}}int nn=node2.size()-1;CDQ(1,nn);int ans=0;for(int i=1;i<=nn;i++)if(!vis[node2[i].id])ans+=node2[i].num;printf("Case #%d: %d\n",++kase,ans);}return 0;
}
HDU - 5517 Triple(三维偏序-二维树状数组/CDQ分治)相关推荐
- HDU 5517---Triple(二维树状数组)
题目链接 Problem Description Given the finite multi-set A of n pairs of integers, an another finite mult ...
- HDU 5465 Clarke and puzzle (二维树状数组维护区间异或)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5465 解题思路: 因为要对大量的区间进行异或,所以考虑用二维树状数组就行维护. #include< ...
- hdu 1892二维树状数组
这题我知道是用树状数组,可是好久没打树状数组了,就想用普通方法水过去~~结果--结果--水了好多方法都水不过,出题人真狠呐--我的水方法是对于每一次查询,初始化ans=(x2-x1+1)*(y2-y1 ...
- hdu 1892【二维树状数组】
O(∩_∩)O哈哈~二维树状数组被我搞出来了,太开心了,第一道二维树状数组,(- o -)~zZ 虽然中途还是出问题了,就是S询问的时候我没有考虑坐标的大小问题,还是搜了别人的代码才知道的,看来自己考 ...
- hdu 5465 Clarke and puzzle (二维树状数组+nim博弈)
解析: 利用二维树状数组来区间询问异或和,以及单点更新,然后利用nim博弈的结论判断胜负. mymy codecode #include <cstdio> #include <cst ...
- HDU-4456 Crowd 二维树状数组+坐标转换
题意:给定一个N*N的网格,现在M组操作,一种操作时改变网格上的某个单点的权值,另外一种操作是求到一点曼哈顿距离为小于等于k的所有的权值和,初始化网格所有点的权值为0. 解法:这题如果没有那些特定的条 ...
- 2018山东冬令营:UPC 计数问题 (二维树状数组)
计数问题 时间限制: 1 Sec 内存限制: 128 MB 提交: 185 解决: 51 [提交][状态][讨论版][命题人:admin] 题目描述 一个n*m的方格,初始时每个格子有一个整数权 ...
- 二维树状数组 ----2021广东省赛 ----- K - Kera‘s line segment[区间转二维平面+树状数组维护前缀最小最大值]
题目链接 题目大意: 就是一个一维的数轴上面有一堆线段用一个三元组(l,r,val)(l,r,val)(l,r,val)表示. 现在我们有两个操作: 就是往数轴上面添加线段 询问[L,R][L,R][ ...
- szu 寒训第二天 树状数组 二维树状数组详解,以及树状数组扩展应用【求逆序对,以及动态第k小数】
树状数组(Binary Index Tree) 树状数组可以解决可以转化为前缀和问题的问题 这是一类用以解决动态前缀和的问题 (有点像线段树简版) 1.对于 a1 + a2 + a3 + - + an ...
最新文章
- 什么是Kafka Global Table (GlobalKTable)
- java继承类大全_Java 面向对象继承部分(示例代码)
- View.setSystemUiVisibility(int) 方法使用
- ML之sklearn:sklearn.linear_mode中的LogisticRegression函数的简介、使用方法之详细攻略
- 阿丽塔大脑是机器人哪_阿丽塔——和机器人结合的未来离我们有多远?
- c#多维数组的建立及操作 总结
- [mybatis]映射文件_select_resultMap_关联查询_association分步查询延迟加载
- java序列化深克隆_克隆可序列化和不可序列化的Java对象
- 联想计算机如何设置用户名和密码,联想电脑怎样设密码?联想电脑设置密码方法步骤【图文】...
- NSZombieEnabled使用
- Tomcat 中文路径乱码
- 三范式理解 数据库原理
- 2018-2019-1 20165320 20165325 20165337 实验一 开发环境的熟悉
- Mybatis Plus配置以及单表操作
- 关于AD9371调试笔记
- SVO2.0 安装编译
- 为啥海康摄像头网页无法预览
- MongoDB文档增删改查
- 淮北晨刊报道我校学生募捐送温暖活动
- .7z.001 这种让人头疼的分卷格式