第一式: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. linux下安装php扩展的redis

    bu要在网上顺便找个redis扩展,一是不安全,而是,别人的redis可能只能new,能连接,但是不保证是否能进行其他操作, 1.百度一下php redis扩展. 搜索到这个网址:http://pec ...

  2. 前端 CSS的继承性

    css有两大特性:继承性和层叠性 继承性 继承:给父级设置一些属性,子级继承了父级的该属性,这就是我们的css中的继承. 记住:有一些属性是可以继承下来 : color . font-*. text- ...

  3. Django forms组件的校验

    引入: from django import forms 使用方法:定义规则,例: class UserForm(forms.Form): name=forms.CharField(max_lengt ...

  4. 在react中用装饰器来装饰connect

    假设我们在react中有如下header组件: import React, { PureComponent } from 'react'; import { connect } from 'react ...

  5. 1/n循环节长度

    /* * 求1/i的循环节长度的最大值,i<=n */ ; int res[MAXN]; // 循环节长度 int main() { memset(res, , sizeof(res)); in ...

  6. Neo4j 不区分大小写的模糊查询匹配

    问题:当图数据库中存储的节点的名字为英文时,就会遇到大小写不匹配问题. 使用不区分大小写的正则表示式可以解决以上问题. Cpyher的where语法里支持正则表达式 ,其语法为 :   =~ &quo ...

  7. Sublime text设置快捷键让编写的HTML文件在打指定浏览器预览

    作者:浪人链接:https://www.zhihu.com/question/27219231/answer/43608776来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...

  8. runtime 理解笔记

    runtime 简称运行时,是系统运行的一种机制,在oc中通过c语言编写一个运行系统库.考进行一些非常底层的操作(oc无法完成的). 1.利用runtime,在程序运行过程中,动态创建一个类(比如KV ...

  9. JSP学习(2)

    JSP学习(2) JSP简介 Java Server Page,其根本是一个简单Servlet设计. 常用的动态网站开发技术 JSP:安全性高,适合开发大型的,企业级或分布式的Web应用程序. Asp ...

  10. 你不知道的hostname命令

    一般hostname可以获取主机名,但是hostname实际上可以做更多的事情. 让我们先来看看它的帮助. Usage: hostname [-b] {hostname|-F file} set ho ...