ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

#loj6032. 「雅礼集训 2017 Day2」水箱

2022-07-13 16:02:38  阅读:150  来源: 互联网

标签:loj6032 Day2 雅礼 扫描线 ans 区间 include type 挡板


#loj6032. 「雅礼集训 2017 Day2」水箱

题目描述

给出一个长度为 \(n\) 宽度为 \(1\) ,高度无限的水箱,有 \(n - 1\) 个挡板将其分为 \(n\) 个 \(1 - 1\) 的小格,然后向每个小格中注水,水如果超过挡板就会溢出到挡板的另一边,这里的水是满足物理定律的(在无挡板阻拦的情况下会向低处流),现在有 \(m\) 个条件 \((i, y, k)\) ,表示从左到右数的第 \(i\) 个格子中,在高度为 \(y + 0.5\) 的地方是否有水,\(k = 1\) 表示有水,\(k = 0\) 表示没有水,请求出这 \(m\) 个条件最多能同时满足多少个条件。本题有多组数据。

输入格式

第一行一个正整数 \(T\),为数据组数。
第二行两个正整数 \(n\)、\(m\),中间用空格隔开。
接下来一行 \(n - 1\) 个整数,表示从左到右每一块隔板的高度。
接下来 \(m\) 行,每行三个整数 \(i\)、\(y\)、\(k\),表示一个条件。

输出格式

共 \(T\) 行,每行对应一组数据的答案。

输入数据 1

2
3 4
3 4
1 3 1
2 1 0
2 2 0
3 3 1
2 2
2
1 2 0
1 2 1

输出数据 1

3
1

数据范围与提示

对于 \(20\%\) 的数据,\(n, m \leq 16\);
对于另外 \(10\%\) 的数据,只存在指明某处有水的条件;
对于另外 \(30\%\) 的数据,\(n, m \leq 1000\);
对于 \(100\%\) 的数据,\(n, m \leq 10 ^ 5, T \leq 5\) 。

Solution

这道题是道好题,做法有很多种。博客上有人提供了平衡树启发式合并的做法,也有用线段树优化 \(\text{DP}\) 的做法,但其实还有另一种类似扫描线的做法,可能时间复杂度上更优。

输入的时候将所有挡板的 \(x,y\) 记录下来,并且用 \(type=-1\) 进行标记,然后记录所有条件,无水用 \(type=0\) 标记,有水用 \(type=1\) 标记,然后对所有的坐标按照 \(y\) 为关键字从小到大排序。

然后就可以从下至上用扫描线一路扫上去,显然这样的耗时是 \(\text O(n+m)\) 的。现在就是讨论如何记录答案。根据 \(type\) 的值分成三种情况讨论:

type=-1

此时的 \(y\) 表示的是挡板的高度,而扫描线扫到当前高度表示水已经可以漫过当前挡板,因此需要将挡板左右的两个区间直接合并,合并操作可以直接用并查集实现。对于每一个区间,可以记录两个信息, \(ans[i]\) 表示当前区间的最优答案, \(f[i]\) 表示当前区间 \(type=1\) 的条件的个数。那么在合并区间的时候,需要将被合并区间的 \(ans\) 和 \(f\) 更新到总区间中,并且尝试用 \(ans\) 去更新答案(如果可以)。

type=0

此时的 \(y\) 表示的是没有水的地方的高度,显然既然没有水,那么它哪里都不会影响,所以它只会对 \(ans\) 贡献一个 \(+1\) ,并尝试更新答案。

type=1

此时的 \(y\) 表示的有水的地方的高度,因为有水,而且比它低的位置的 \(type=0\) 的位置在之前已经讨论过了(因为排了序),所以在 \(y\) 的地方放水会导致 \(y\) 所在的区间全部被水淹掉,此时满足的条件数量全部来自于此区间中的 \(type=1\) 的条件,即 \(f\) 。因此,需要用 \(f\) 去尝试更新 \(ans\) 并用 \(ans\) 尝试更新答案。

其实看整个算法流程,很像树形 \(\text{DP}\) 。因为这样的扫描线相当于是对所有挡板隔成的区间建了一棵二叉树,然后从叶子结点向上按深度逐层扫描上去,最后将答案合并统计在根节点上。区间由线段组成,所以我们就叫它线段树形区间 \(\text{DP}\) 吧

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<limits.h>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
template<typename T> void read(T &k)
{
 	k=0;
	T flag=1;char b=getchar();
	while (b<'0' || b>'9') {flag=(b=='-')?-1:1;b=getchar();}
	while (b>='0' && b<='9') {k=(k<<3)+(k<<1)+(b^48);b=getchar();}
	k*=flag;
}
const int _SIZE=1e5;
int T,n,m;
struct EVENT{
	int type,x,y;
}point[(_SIZE<<1)+5];
int tot;
int fa[_SIZE+5],f[_SIZE+5],ans[_SIZE+5],res;
bool cmp(EVENT a,EVENT b)
{
	if (a.y!=b.y) return a.y<b.y;
	if (a.type!=b.type) return a.type<b.type;
	return a.x<b.x;
}
int find(int x)
{
	if (fa[x]) return fa[x]=find(fa[x]);
	return x;
}
void solve()
{
	tot=0;mem(fa,0);mem(f,0);mem(ans,0);mem(point,0);
	read(n),read(m);
	for (int i=1;i<n;i++) 
	{
		read(point[++tot].y);
		point[tot].type=-1;
		point[tot].x=i;
	}
	for (int i=1;i<=m;i++)
	{
		read(point[++tot].x);
		read(point[tot].y);
		read(point[tot].type);
	}
	sort(point+1,point+tot+1,cmp);
	//system("pause");
	res=0;
	for (int i=1;i<=tot;i++)
	{
		int type=point[i].type,x=point[i].x;
		if (type==-1)
		{
			int f_1=find(x),f_2=find(x+1);
			fa[f_2]=f_1;
			f[f_1]+=f[f_2];
			ans[f_1]+=ans[f_2];
			res=max(ans[f_1],res);
		}
		else if (!type)
			res=max(res,++ans[find(x)]);
		else
		{
			int f_=find(x);
			++f[f_];
			ans[f_]=max(ans[f_],f[f_]);
			res=max(res,ans[f_]);
		}
	}
	printf("%d\n",res);
}
int main()
{
	read(T);
	while (T--)
		solve();
	return 0;
}

我到底该把这道题放在什么标签下啊……

标签:loj6032,Day2,雅礼,扫描线,ans,区间,include,type,挡板
来源: https://www.cnblogs.com/hanx16msgr/p/16474216.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有