第一式: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. Creat-React-Native-App 之StackNavigator之踩坑记录

    Creat-React-Native-App简称CRNA. 在我开始入门RN时fb已经推出和Expo联合开发用于快速创建React Native应用的工具: Create-React-Native-A ...

  2. Java提取文本文档中的所有网址(小案例介绍正则基础知识)

    正则表达式基础以及Java中使用正则查找 定义: 正则表达式是一些用来匹配和处理文本的字符串 正则的基础(先大致了解下) 1. 正则表达式的作用 查找特定的信息(搜索) 替换一些文本(替换) 2. 正 ...

  3. 转载-linux挂载的意思

    挂载:Liunx采用树形的文件管理系统,也就是在Linux系统中,可以说已经没有分区的概念了.分区在Linux和其他设备一样都只是一个文件.要使用一个分区必须把它加载到文件系统中.这可能难于理解,继续 ...

  4. [19/05/18-星期六] HTML_form标签

    一.form标签(一) <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> & ...

  5. <每日一题> Day2:CodeForces-1141C.PolycarpRestoresPermutation(思维题)

    原题链接 参考代码: #include <iostream> #include <cstring> using namespace std; + , INF = 0x3f3f3 ...

  6. [2019杭电多校第八场][hdu6667]Roundgod and Milk Tea

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6667 题目大意是说n个班级,每个班级有ai人和bi杯茶,每个人只能喝其他班的茶并且只能喝一杯.问最多有 ...

  7. [ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

    原文:[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式 正如我们在<依赖注入:控制反转>提到过的,很多人将IoC理解为一种“面向对象的设计模式”,实际上IoC不仅与面向对象没 ...

  8. 05-CSS浮动、定位、页面布局

    # 浮动 ### 文档流文档流,是指盒子按照html标签编写的顺序依次从上到下,从左到右排列,块元素占一行,行内元素在一行之内从左到右排列,先写的先排列,后写的排在后面,每个盒子都占据自己的位置. # ...

  9. Raven2

     Raven2实验 0x01 寻找IP 本机IP:56.130 1. 使用 nmap -sn "ip6"#主机发现(不进行端口扫描) https://cloud.tencent.c ...

  10. ORI-621龙芯3A处理器CPCI刀片计算机

    ORI-621龙芯3A处理器CPCI刀片计算机 一.产品简介 ORI -621是一款基于龙芯3A国产CPU处理器的特种CPCI刀片计算机.该产品成功地实现了服务器NUMA架构在国产特种计算机中的应用, ...