[JZOJ5229]【GDOI2018模拟7.14】小奇的糖果
题目
题目大意
在一个二维的平面上,有一堆有颜色的点,你需要找出一条水平线段,使得这个线段上面(或者是下面)的点的颜色不包含所有的颜色。问点数最大是多少。
思考历程
在一开始,我看错了题目大意。
题目说的是线段,而我理解的是直线。
然后推了好多遍样例,觉得样例错了。
后来才发现题目给的是线段。
不然这题就是一个大大的水题了
估计一下时间复杂度,嗯,应该是O(nlgn)O(n \lg n)O(nlgn)或O(nlg2n)O(n \lg^2 n)O(nlg2n)。
往这个方面想了好久,想不出来。
于是退而求其次,想O(n2)O(n^2)O(n2)
这个时间复杂度还是很容易做的。
显然地,我们肯定要先以纵坐标排一遍序。
然后从上往下扫(后面还要从上往下扫)。
对于每个横坐标,记录一下当前被扫过的点。
那么,相当于是,在扫描线上,找到一个合法区间,使得这个区间最大。
我们可以先固定左端点,然后右端点尽量延伸。用一个桶就可以了,还比较简单。
然后就愉快地拿到了60分了。
正解
正解是O(nlgn)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】小奇的糖果的更多相关文章
- 【BZOJ4548】小奇的糖果
→原题传送门←(by Hzwer) 「题目背景」 小奇不小心让糖果散落到了地上,它对着满地的彩色糖果胡思乱想. 「问题描述」 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或 ...
- 【BZOJ-4548&3658】小奇的糖果&Jabberwocky 双向链表 + 树状数组
4548: 小奇的糖果 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 103 Solved: 47[Submit][Status][Discuss] ...
- 【BZOJ4548】小奇的糖果 set(链表)+树状数组
[BZOJ4548]小奇的糖果 Description 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的 ...
- 【题解】BZOJ4548 小奇的糖果(树状数组)
[题解]BZOJ4548 小奇的糖果(树状数组) 说在前面:我有个同学叫小奇,他有一个朋友叫达达,达达特爱地理和旅游,初中经常AK地理,好怀恋和他已经达达一起到当时初中附近许多楼盘的顶楼逛的时光... ...
- 【NOIP模拟赛】小奇的矩阵
[题目背景] 小奇总是在数学课上思考奇怪的问题. [问题描述] 给定一个n*m的矩阵,矩阵中的每个元素aij为正整数. 接下来规定 1.合法的路径初始从矩阵左上角出发,每次只能向右或向下走,终点为右下 ...
- 【NOIP模拟赛】小奇挖矿 2
[题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿石交易市场,以便为飞船升级无限非概率引擎. [问题描述] 现在有m+1个星球,从左到右标号为0到m,小奇最初在0 ...
- 小奇的糖果(candy)
[题目背景]小奇不小心让糖果散落到了地上,它对着满地的彩色糖果胡思乱想.[问题描述]有 N 个彩色糖果在平面上. 小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果 ...
- BZOJ 4548 小奇的糖果
Description 有 \(N\) 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的颜色. Input 包含 ...
- BZOJ4548 小奇的糖果
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...
随机推荐
- Arthas 3.1.2 版本发布 | 增加 logger/heapdump/vmoption 命令
最近偶尔有用户反馈某些 HTTP 接口出现超时问题,而 web 服务端的 Trace 监控没有出现 http 返回值为 503 等异常情况.出现这种情况一般是web容器出现问题,客户端连 Arthas ...
- 0926CSP-S模拟测试赛后总结
又一次垫底.持续低迷.20分. 赛时状态还可以.但是过于保守而不思进取.三道题目打了暴力就滚粗了. 暴力还挂掉了. T1暴力因为开小了数组挂成了0.1000的点,子序列个数我开了1e5以为足够了.结果 ...
- duilib库分析1.消息流程分析
看下CWindowWnd类与CPaintManagerUI类是咋进行消息分发的吧. 1. 先看下CPaintManagerUI类的MessageLoop函数: void CPaintManagerUI ...
- POJ 2104:K-th Number 整体二分
感觉整体二分是个很有趣的东西. 在别人的博客上看到一句话 对于二分能够解决的询问,如果有多个,那么如果支持离线处理的话,那么就可以使用整体二分了 树套树写了一天还是WA着,调得焦头烂额,所以决定学cd ...
- VS2010-MFC(图形图像:CDC类及其屏幕绘图函数)
转自:http://www.jizhuomi.com/software/244.html 上一节讲了文本输出的知识,本节的主要内容是CDC类及其屏幕绘图函数. CDC类简介 CDC类是一个设备上下文类 ...
- Mysql优化系列之查询性能优化前篇1
前言 这是优化系列的最后一篇的第1小篇,我们其实可以直接从sql怎么写讲起,why not?但是我还是决定花2个篇幅 问一些问题,带着几个问题循序渐进的往下走. 一个sql语句是怎么被执行的? sql ...
- spring boot中使用javax.validation以及org.hibernate.validator校验入参
这里springboot用的版本是:<version>2.1.1.RELEASE</version> 自带了hibernate.validator,所以不用添加额外依赖 1.创 ...
- AQS(队列同步器)
目录导引: 一.简介 二.源码解析(JDK8) 三.运用示例 一.简介 1.volatile volatile修饰的共享变量可以保证可见性和有序性(禁止指令重排序). 2.CAS: CAS的原理很简单 ...
- Lost's revenge HDU - 3341 AC自动机+DP(需要学会如何优雅的压缩状态)
题意: 给你n个子串和一个母串,让你重排母串最多能得到多少个子串出现在重排后的母串中. 首先第一步肯定是获取母串中每个字母出现的次数,只有A T C G四种. 这个很容易想到一个dp状态dp[i][A ...
- selenium基础(下拉菜单操作)
selenium基础(下拉菜单操作) 非select/option元素: 1.触发下拉列表出现 2.等待下拉列表中的元素出现,然后进行选择元素即可. select/option元素: 下拉框操作-Se ...