题目

题目大意

在一个二维的平面上,有一堆有颜色的点,你需要找出一条水平线段,使得这个线段上面(或者是下面)的点的颜色不包含所有的颜色。问点数最大是多少。


思考历程

在一开始,我看错了题目大意。

题目说的是线段,而我理解的是直线。

然后推了好多遍样例,觉得样例错了。

后来才发现题目给的是线段。

不然这题就是一个大大的水题了

估计一下时间复杂度,嗯,应该是O(nlg⁡n)O(n \lg n)O(nlgn)或O(nlg⁡2n)O(n \lg^2 n)O(nlg2n)。

往这个方面想了好久,想不出来。

于是退而求其次,想O(n2)O(n^2)O(n2)

这个时间复杂度还是很容易做的。

显然地,我们肯定要先以纵坐标排一遍序。

然后从上往下扫(后面还要从上往下扫)。

对于每个横坐标,记录一下当前被扫过的点。

那么,相当于是,在扫描线上,找到一个合法区间,使得这个区间最大。

我们可以先固定左端点,然后右端点尽量延伸。用一个桶就可以了,还比较简单。

然后就愉快地拿到了60分了。


正解

正解是O(nlg⁡n)O(n \lg n)O(nlgn)的。

假设我们取的是扫描线上面的点(下面的道理是一样的)。

我们先将扫描线移到最下方,很显然,在这时候所有点都在扫描线的上面,这就是它们的终极状态。

用一个树状数组维护一段横坐标的区间中的点数。

在用一个双向链表来维护一下在它左边的离他最近的同颜色的点,右边同理。

那么,对于一个点,它的前驱和它之间没有和它们同颜色的点,右边同理。

我们先把终极状态的答案求出来,即是枚举哪个颜色不选,然后在没有这些颜色的区间中用树状数组求出点数,试着更新答案。

接着我们考虑将扫描线向上移动。

在移动的时候,有一些点去到了扫描线的下面,那么我们就要将它们在树状数组中的贡献减去,将它们从双向链表中删去。

一个点从双向链表中删去后,它之前的前驱和后继之间没有和它颜色相同的点,所以我们可以这个区间试着更新答案。

然后整一题就如此愉快地解决了。

其中有一个很尴尬的地方是,由于一条扫描线可能同时扫过多个点,然而在统计这些答案的时候可能会对互相有影响。有点脑抽,然后想了很久,最终惊奇的发现,其实……

我们可以先在树状数组中减去它的贡献,在扫描线换行的时候,我们就将它们从双向链表中删去,并统计答案。

由于我们已经在树状数组中减去了它的贡献,那么在后面,如果一个点本应被删除,但是还没有删除就在前面的点中多计算了它的贡献,那么这个贡献是会被覆盖的。


吐槽

最近总喜欢吐槽一下,什么东西都吐槽一下。

打代码的时候细节可能比较多,有时没有注意它在排序前后的对应位置,导致了我的程序调了好久,很不爽……

(我的程序中,先对横坐标进行了排序,预处理了双向链表,然后再按纵坐标排序。可是,在排序前后的序号我在一开始忘记要对上,于是……)

还有,为什么明明是我讲题,却在一群人对了这题之后才对?


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 100000
int n,m,tot;
struct Candy{
int x,y,c;
int num;
} d[N+10];
inline bool cmpx(const Candy &a,const Candy &b){
return a.x<b.x;
}
inline bool cmpy(const Candy &a,const Candy &b){
return a.y<b.y;
}
int ans;
int t[N+10];
#define lowbit(x) ((x)&(-(x)))
inline int add(int k,int x){
for (;k<=m;k+=lowbit(k))
t[k]+=x;
}
inline int query(int k){
int res=0;
for (;k;k-=lowbit(k))
res+=t[k];
return res;
}
int w[N+10];//w[i]表示编号i的点(以横坐标排序之后的顺序)的编号
int l[N+10],r[N+10],tmpl[N+10],tmpr[N+10];//这些东西记录的都是以横坐标排序后的编号
int last[N+10];//记录某种颜色最后的点的编号
inline void init(){
sort(d+1,d+n+1,cmpx);
m=0;
for (int i=1,lst=-2147483648;i<=n;++i){
if (d[i].x!=lst)
m++,lst=d[i].x;
d[i].x=m;
}
//以上这段是离散化(如果你开心,不离散化打动态开点线段树,我也没意见)
for (int i=1;i<=n;++i){
d[i].num=i;
w[i]=d[i].x;
}
memset(last,0,sizeof last);
for (int i=1;i<=n;++i){
l[i]=last[d[i].c];
r[l[i]]=i;
last[d[i].c]=i;
}
for (int i=1;i<=tot;++i)
r[last[i]]=n+1;
memcpy(tmpl,l,sizeof l);
memcpy(tmpr,r,sizeof r);
}
int main(){
int T;
scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&tot);
for (int i=1;i<=n;++i)
scanf("%d%d%d",&d[i].x,&d[i].y,&d[i].c);
init();
for (int i=1;i<=n;++i)
add(d[i].x,1);
w[0]=0;
w[n+1]=m+1;
ans=0;
for (int i=1;i<=tot;++i){
if (w[last[i]]+1<=m+1)
ans=max(ans,query(m)-query(w[last[i]]));
for (int j=last[i];j;j=l[j])
if (w[l[j]]+1<w[j])
ans=max(ans,query(w[j]-1)-query(w[l[j]]));
}
//以上是统计全部的答案(就是扫描线在全部点上面或下面的情况)
sort(d+1,d+n+1,cmpy);
d[0].y=d[n+1].y=-2147483648;
for (int i=1,j=1;i<=n;++i){
add(d[i].x,-1);
if (d[i].y!=d[i+1].y)
for (;j<=n && d[j].y<=d[i].y;++j){
int jj=d[j].num;
r[l[jj]]=r[jj];
l[r[jj]]=l[jj];
if (w[l[jj]]+1<w[r[jj]])
ans=max(ans,query(w[r[jj]]-1)-query(w[l[jj]]));
}
}
memset(t,0,sizeof t);
swap(l,tmpl),swap(r,tmpr);
for (int i=1;i<=n;++i)
add(d[i].x,1);
for (int i=n,j=n;i>=1;--i){
add(d[i].x,-1);
if (d[i].y!=d[i-1].y)
for (;j>=1 && d[j].y>=d[i].y;--j){
int jj=d[j].num;
r[l[jj]]=r[jj];
l[r[jj]]=l[jj];
if (w[l[jj]]+1<w[r[jj]])
ans=max(ans,query(w[r[jj]]-1)-query(w[l[jj]]));
}
}
printf("%d\n",ans);
}
return 0;
}

总结

有的时候链表是一个好东西。

比如说在处理有没有相同颜色之类的问题的时候,用链表记录一下左右最近的同颜色的点或许是一个很好的解题方向。

有的时候正着搞不容易,那就反着搞,全部加进去,然后删掉。

还有在做平面问题时应当往扫描线上想一想。

[JZOJ5229]【GDOI2018模拟7.14】小奇的糖果的更多相关文章

  1. 【BZOJ4548】小奇的糖果

    →原题传送门←(by Hzwer) 「题目背景」 小奇不小心让糖果散落到了地上,它对着满地的彩色糖果胡思乱想. 「问题描述」 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或 ...

  2. 【BZOJ-4548&3658】小奇的糖果&Jabberwocky 双向链表 + 树状数组

    4548: 小奇的糖果 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 103  Solved: 47[Submit][Status][Discuss] ...

  3. 【BZOJ4548】小奇的糖果 set(链表)+树状数组

    [BZOJ4548]小奇的糖果 Description 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的 ...

  4. 【题解】BZOJ4548 小奇的糖果(树状数组)

    [题解]BZOJ4548 小奇的糖果(树状数组) 说在前面:我有个同学叫小奇,他有一个朋友叫达达,达达特爱地理和旅游,初中经常AK地理,好怀恋和他已经达达一起到当时初中附近许多楼盘的顶楼逛的时光... ...

  5. 【NOIP模拟赛】小奇的矩阵

    [题目背景] 小奇总是在数学课上思考奇怪的问题. [问题描述] 给定一个n*m的矩阵,矩阵中的每个元素aij为正整数. 接下来规定 1.合法的路径初始从矩阵左上角出发,每次只能向右或向下走,终点为右下 ...

  6. 【NOIP模拟赛】小奇挖矿 2

    [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿石交易市场,以便为飞船升级无限非概率引擎. [问题描述] 现在有m+1个星球,从左到右标号为0到m,小奇最初在0 ...

  7. 小奇的糖果(candy)

    [题目背景]小奇不小心让糖果散落到了地上,它对着满地的彩色糖果胡思乱想.[问题描述]有 N 个彩色糖果在平面上. 小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果 ...

  8. BZOJ 4548 小奇的糖果

    Description 有 \(N\) 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的颜色. Input 包含 ...

  9. BZOJ4548 小奇的糖果

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...

随机推荐

  1. C# - Finalize 和 Dispose

    重要: https://www.cnblogs.com/Jessy/articles/2552839.html https://blog.csdn.net/daxia666/article/detai ...

  2. expect离线安装

    expect5.45.4.tar.gz和tcl8.4.11-src.tar.gz压缩包请前往以下链接下载: https://download.csdn.net/download/gangzi221/1 ...

  3. Delphi窗体间发送消息或字符串

    在Delphi 开发中,常常应用到窗体消息传递,以达成某种操作要求,以下列举一个应用的例子,供大家参考. 自定义过程/函数方法://发送字符串到指字句柄的窗口中 (接收窗体需用发送时的消息常量WM_C ...

  4. [BJOI 2018]染色

    题意:求01成立. 并查集维护,记录一个变量判断决策. #include<bits/stdc++.h> using namespace std; #define int long long ...

  5. com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections"

    报错: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected estab ...

  6. docker 可持续集成及日志管理及监控报警

  7. PAT甲级——A1131 Subway Map【30】

    In the big cities, the subway systems always look so complex to the visitors. To give you some sense ...

  8. IIR滤波器数字频带转换

    <DSP using MATLAB>(Ingle & John Proakis)3ed,书中表8.2似乎不对. <Discrete Time signal processin ...

  9. Hadoop2.7.1配置NameNode+ResourceManager高可用原理分析

    关于NameNode高可靠需要配置的文件有core-site.xml和hdfs-site.xml 关于ResourceManager高可靠需要配置的文件有yarn-site.xml 逻辑结构: Nam ...

  10. [记]Cordova安装插件选择插件版本

    在项目中可以使用 cordova plugin add [PLUGIN_ID] 這个命令安装一个cordova插件,这个命令好像是安装插件的最新版本.当需要通过cordova下载这个插件一个特定的版本 ...