第一式:https://ac.nowcoder.com/acm/contest/143/I

题意:

有 n 个点,一个点集 S 是好的,当且仅当对于他的每个子集 T,存在一个右边无限长的矩形,使得这个矩形包含了 T,但是和 S-T 没有交
   求这 n 个点里有几个好的点集
1<=n<=10^5

1):当只选取1个点时,我们可以发现,任何一个点都满足题意。

2):当我们选取2个点时,我们可以发现如果要满足一个无限向右的矩形只框住一个点,当且仅当两个点的纵坐标不相同。因此,对于选2个点的总的方案数等于C(n,2)-C(纵坐标相同的个数,2)

3):当我们选3个点的时候(假设三个点为a,b,c),我们可以发现,当我们选取{a,b}作为子集,倘如第三个点c在{a,b}的右边,则我们发现由{a,b}组成的矩形一定包含{c},故不成立。因此{c}必定在{a,b}的左边,即当且仅当三个点的能够构成一个'<'号的形式才能够符合题意。

4):当选4个点及以上时,我们发现不管怎么样摆,均不可能出现3)的情况,故4个点以上的点是不合理的。

因此现在我们只需要处理的就是3)中的情况。对于3)的情况。我们只要求出在第i个点之前,有多少个点的x坐标比当前点大(记位below),再求出在第i个点之后有多少个点的x坐标比当前点大(记位above),那么对于第i个点而言,该点的方案数即为below*above了。

而对于below和above值的维护,我们需要先将y坐标进行离散化,然后将数组按照x坐标进行排序,然后用树状数组对区间进行维护即可。

现在问题的转化为在平面上有多少个不同的3对点集可以构成 "<" 的形状

现在观察此图可以发现:

对于包括点1的点集的情况无非就是 在点1上面的点与在点1下面的点来构成,那总的答案就一个组合的问题 cnt1*xcnt2;

所以我们就要只要在点1上面的点的个数与在点一下面点的个数(还要保证是在点1的右边)

所以很自然的想到对x排序,然后从x大开始便利(因为比当前点的 x小的点是没有价值的),用树状树状维护一个y上面的前缀和既可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = ;
const int maxn=;
ll n;
struct no
{
int x,y;
}a[maxn];
int tree[maxn*],b[maxn];
ll box[maxn];
void add(int x , int c)
{
while(x<=n)
{
tree[x]+=c;
x+=(x&(-x));
}
}
int sum(int x)
{
int ret=;
while(x)
{
ret+=tree[x];
x-=(x&(-x));
}
return ret;
}
bool cmp(no a , no b)
{
return a.x>b.x;
}
int main()
{
scanf("%lld",&n);
for(int i= ; i<=n ; i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
b[i]=a[i].y;
}
sort(b+,b++n);
for(int i= ; i<=n ; i++)
{
a[i].y=lower_bound(b+,b++n,a[i].y)-b;
box[a[i].y]++;
}
ll ans=n+n*(n-)/; ///统计一个点和两个点的情况
for(int i= ; i<=n ; i++)
ans-=box[i]*(box[i]-)/; ///减去两个点有相同的y
ans=(ans+mod)%mod;
///统计 "<" 的情况
int L=1;
sort(a+,a++n,cmp); for(int i= ; i<=n ; i++)
{
ll down=sum(a[i].y-);
ll up=sum(n)-sum(a[i].y);
ans=(ans+down*up)%mod;
if(a[i].x!=a[i+].x)
{
for(;L<=i ; L++)
add(a[L].y,);
}
}
printf("%lld\n",ans);
return ;
}

第二式:http://codeforces.com/contest/1191/problem/F

题意: 给出在二维平面上的点,问有多少个上面无限延长的矩阵是不同的 , 不同的定义为矩阵里面的点集是不同的

分析:这需要对x,y进行一个离散化的操作:

注意到其实从上往下一行一行扫过去,每次必须新增的元素才是新的集合,那很容易想到一个不重不漏的办法就是每次计算“以点p[i]为加进去的新点中的结束的集合”,那么假设一开始p[i]的左侧有cntl个点,那么显然有(cntl+1)条线在p[i]的左侧,而p[i]的右侧有cntr个点,也是(cntr+1)条线。

这个cntl显然就是query(1,p[i].x-1),而右侧则是query(p[i].x+1,p[i+1].x-1),因为不能包含同y的下一个点p[i+1],而其中,上面的点选法也会产生区别。

这个线段树的更新操作是赋值而不是加+1 , (因为如果同一条竖线上有多个点那其实排序去选择也只有一个价值而已)

那么每层加入一个正无穷也就是xn+1就可以了。

结合这图可以很容易看懂线段树的操作:1点的答案为(3*3) , 同时也可以理解为什么右侧是query(p[i].x+1,p[i+1].x-1),而不是说从当前点到最右面 , 想象一下,如果点1和点2右边的价值都是如此计算,那必然会存在重复计算。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int n;
struct Point {
int x, y;
bool operator<(const Point &p)const {
return y == p.y ? x<p.x: y>p.y;
//y从上到下,x从左到右
}
} p[]; int x[];
int y[]; ll sum; const int MAXM = ;
int st[(MAXM << ) + ]; inline void push_up(int o) {
st[o] = st[o << ] + st[o << | ];
} void build(int o, int l, int r) {
if(l == r) {
st[o] = ;
} else {
int m = (l + r) >> ;
build(o << , l, m);
build(o << | , m + , r);
push_up(o);
}
} void update(int o, int l, int r, int x, int v) {
if(l == r) {
//不是加,是赋值,同x的点是没有差别的
st[o] = v;
return;
} else {
int m = (l + r) >> ;
if(x <= m)
update(o << , l, m, x, v);
else if(x >= m + )
update(o << | , m + , r, x, v);
push_up(o);
}
} int query(int o, int l, int r, int a, int b) {
if(b < a)
return ;
else if(a <= l && r <= b) {
return st[o];
} else {
int m = (l + r) >> ;
int ans = ;
if(a <= m)
ans = query(o << , l, m, a, b);
if(b >= m + )
ans += query(o << | , m + , r, a, b);
return ans;
}
} int vx[], vxtop; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
//freopen("Yinku.out", "w", stdout);
#endif // Yinku
while(~scanf("%d", &n)) {
for(int i = ; i <= n; i++) {
scanf("%d%d", &p[i].x, &p[i].y);
x[i] = p[i].x;
y[i] = p[i].y;
}
sort(x + , x + + n);
int xn = unique(x + , x + + n) - (x + );
sort(y + , y + + n);
int yn = unique(y + , y + + n) - (y + );
for(int i = ; i <= n; i++) {
p[i].x = lower_bound(x + , x + + xn, p[i].x) - x;
p[i].y = lower_bound(y + , y + + yn, p[i].y) - y;
//从1开始分配新的坐标
//printf("(%d,%d)\n", p[i].x, p[i].y);
}
sort(p + , p + + n);
//扫描线
sum = ;
build(, , xn + );
int beg = , cur = ;
while(beg <= n) {
vxtop = ;
while(p[cur].y == p[beg].y) {
update(, , xn + , p[cur].x, );
vx[++vxtop] = p[cur].x;
/*
//点是不会重合的,那包含这个最左侧的点的都是全新集合
int cntl = query(1, 1, xn, 1, p[cur].x - 1);
//在这个点的左侧有cntl个x不同的点,那就有cntl+1个位置
//sum += (cntl + 1); X
//是以这个点为右侧边界的,所以右侧没得选 X
*/
//该层y中是以这个x点为右侧边界,但是两个x点之间的上层y也是可选的
cur++;
}
vx[++vxtop] = xn + ;
for(int i = ; i <= vxtop - ; i++) {
//该层最右端的新点为vx[i]的数量
int cntl = query(, , xn + , , vx[i] - );
///同层或者上层数量
int cntr = query(, , xn + , vx[i] + , vx[i + ] - );
///在vx[i] + 1, vx[i + 1] - 1 横坐标范围 上层的数量,
sum += 1ll * (cntl + ) * (cntr + );
// printf("sum=%lld vx[i]=%d cnt1=%d cnt2=%d\n",sum,vx[i],cntl,cntr,);
}
beg = cur; }
printf("%lld\n", sum);
}
}

第三式:http://codeforces.com/contest/1194/problem/E

题意:

给N条线段 , 线段只有垂直或者水平 , 问有多少个不同的4个线段集合构成(

h1和h2是水平;

v1和v2是垂直的;

h1段与v1段相交;

h2段与v1段相交;

h1段与v2段相交;

h2段与v2段相交。

)  也就是一个#差不多:

分析:在代码里面有注释了, 觉得看了代码理解起来不难

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+;
int c[N];
int n;
int lowbit(int x)
{
return x&(-x);
}
void add(int i , int x)
{
while(i<N)
{
c[i]+=x;
i+=lowbit(i);
}
}
int sum(int i)
{
int ans=;
while(i)
{
ans+=c[i];
i-=lowbit(i);
}
return ans;
}
struct svno
{
int y1,y2,x;
bool operator <(const svno& Q) const
{
return x<Q.x;
}
};
struct hvno
{
int x1,x2,y;
bool operator <(const hvno& Q) const
{
return x2<Q.x2;
}
};
vector<svno> SV;
vector<hvno>HV;
int main()
{
while(~scanf("%d",&n))
{
memset(c,,sizeof(c));
HV.clear();
SV.clear();
ll ans=;
for(int i= ; i<n ; i++)
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1+= , x2+= , y1+= , y2+=;
if(x1==x2)
{
if(y1>y2) swap(y1,y2);
SV.push_back({y1,y2,x1});
}
else
{
if(x1>x2) swap(x1,x2);
HV.push_back({x1,x2,y1});
}
sort(SV.begin(),SV.end());
sort(HV.begin(),HV.end());
}
vector<int>temp;
int lensv=SV.size();
int lenhv=HV.size();
///枚举第一条竖线
for(int i= ; i<lensv ; i++)
{
///加入符合的横线
temp.clear();
for(int j= ; j<lenhv ; j++)
{
if(HV[j].x1<=SV[i].x&&HV[j].x2>=SV[i].x&&HV[j].y>=SV[i].y1&&HV[j].y<=SV[i].y2)
{
temp.push_back(j);
add(HV[j].y,); }
}
///枚举第二条竖线
int lent=temp.size();
int k=;
for(int j=i+ ; j<lensv ; j++)
{
///删除枚举的第二条竖线时不满足条件的横线
///其实只要考虑横线在枚举的竖线的左边的情况
for( ; k<lent ; k++)
{
int k_id=temp[k];
if(HV[k_id].x2<SV[j].x)
{
add(HV[k_id].y,-);
}
else break;
}
///计算价值
ll cnt=sum(SV[j].y2)-sum(SV[j].y1-);
ans+=cnt*(cnt-)/;
// cout<<cnt<<endl;
}
///删除剩余的横线
for(;k<lent;k++)
{
add(HV[temp[k]].y,-);
}
}printf("%lld\n",ans);
}
}

总的来说这种题目不难 , 有时间还是可以想到一些东西的 。Orz

关于线段树or 树状树状 在二维平面搞事情!Orz的更多相关文章

  1. HDU 5877 dfs+ 线段树(或+树状树组)

    1.HDU 5877  Weak Pair 2.总结:有多种做法,这里写了dfs+线段树(或+树状树组),还可用主席树或平衡树,但还不会这两个 3.思路:利用dfs遍历子节点,同时对于每个子节点au, ...

  2. dfs序+主席树 或者 树链剖分+主席树(没写) 或者 线段树套线段树 或者 线段树套splay 或者 线段树套树状数组 bzoj 4448

    4448: [Scoi2015]情报传递 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 588  Solved: 308[Submit][Status ...

  3. P5666-[CSP-S2019]树的重心【树状数组】

    正题 题目链接:https://www.luogu.com.cn/problem/P5666 题目大意 给出\(n\)个点的一棵树,对于每条边割掉后两棵树重心编号和. \(1\leq T\leq 5, ...

  4. LuoguP3834 【模板】可持久化线段树 1(主席树)|| 离散化

    题目:[模板]可持久化线段树 1(主席树) 不知道说啥. #include<cstdio> #include<cstring> #include<iostream> ...

  5. 洛谷P3834 [模板]可持久化线段树1(主席树) [主席树]

    题目传送门 可持久化线段树1(主席树) 题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定 ...

  6. luogu3703 [SDOI2017]树点涂色(线段树+树链剖分+动态树)

    link 你谷的第一篇题解没用写LCT,然后没观察懂,但是自己YY了一种不用LCT的做法 我们考虑对于每个点,维护一个fa,代表以1为根时候这个点的父亲 再维护一个bel,由于一个颜色相同的段一定是一 ...

  7. 【洛谷P3834】(模板)可持久化线段树 1(主席树)

    [模板]可持久化线段树 1(主席树) https://www.luogu.org/problemnew/show/P3834 主席树支持历史查询,空间复杂度为O(nlogn),需要动态开点 本题用一个 ...

  8. 【bzoj1036】树的统计[ZJOI2008]树链剖分+线段树

    题目传送门:1036: [ZJOI2008]树的统计Count 这道题是我第一次打树剖的板子,虽然代码有点长,但是“打起来很爽”,而且整道题只花了不到1.5h+,还是一遍过样例!一次提交AC!(难道前 ...

  9. 洛谷$P2572\ [SCOI2010]$ 序列操作 线段树/珂朵莉树

    正解:线段树/珂朵莉树 解题报告: 传送门$w$ 本来是想写线段树的,,,然后神仙$tt$跟我港可以用珂朵莉所以决定顺便学下珂朵莉趴$QwQ$ 还是先写线段树做法$QwQ$? 操作一二三四都很$eas ...

随机推荐

  1. 前端004/React常用UI组件

    每天进步一点点〜 Ant Design of React //蚂蚁金服设计平台.需要应用何种类型组件可参考API React + mobx + nornj 开发模式文件说明: [1].A.t.html ...

  2. 创建Maven项目时,出现系列的错误提示的修改方法

    1.创建Maven项目成功之后,需要修改一些配置, (1).java版本改为“本系统中java的版本号” 问题一:(2).Dynamic Web Module的version要改为2.5以上,然而本人 ...

  3. java_第一年_JDBC(6)

    DataBaseMetaData对象:由Connection.getDataBaseMetaData()方法获得,可以用来获取数据库的元数据,提供的方法有: getURL():返回一个String类, ...

  4. Ribbon远程调用

    Ribbon是客户端的负载均衡机制,它有几种负载均衡机制.默认是轮询,我们也可以自定义规则.通过合理的分配网络请求来减小服务器的压力.项目都是注册到eureka服务器上.通过ribbon去调用其他服务 ...

  5. ftp服务端

    #coding=utf-8 import SocketServer import json import os class MyTcpHandler(SocketServer.BaseRequestH ...

  6. 如何设置 ComboBox 下拉列表的高度或间距

    ComboBox 的下拉列表部分总是很挤,看起不舒服,但是设置了 ItemHeight 没用,怎么办呢? 首先设置一个较大的 ItemHeight 值,比如 20: 然后设置 ComboBox 的 D ...

  7. layui在当前页面弹出一个iframe层,并改变这个iframe层里的一些内容

    layer.open({ type: 2, title: "专家信息", area: ['100%', '100%'], content: '/ZhuanJiaKu/AddZhua ...

  8. Python3安装教程

    目录 1. 推荐阅读 2. 安装包下载 3. 安装步骤 1. 推荐阅读 Python基础入门一文通 | Python2 与Python3及VSCode下载和安装.PyCharm破解与安装.Python ...

  9. python面向对象--包装标准类型及组合方式授权

    # 实现授权是包装的一个特性.包装一个类型通常是对已存在的类型进行一些自定义定制, # 这种做法可以新建,修改,或删除原有产品的某些功能,而其他的保持不变. # 授权的过程,其实也就是所有的更新功能都 ...

  10. uiautomatorviewer不能直接截取手机屏幕信息

    本身可以用sdk——>tools里自带的ui automator viewer截取如果截取不了,采用以下方法: 新建一个文本文档,名字自己起如uni.bat(注意把后缀给改成.bat) adb ...