首页 > 其他分享 >P4416 [COCI2017-2018#1] Plahte 题解

P4416 [COCI2017-2018#1] Plahte 题解

时间:2023-02-24 13:57:13浏览次数:42  
标签:int 题解 线段 矩阵 COCI2017 num ans P4416 include

题意简述:

给定 \(n\) 个互不相交,可以重叠的矩阵,对某些点染色,这个点上的所有矩阵会被染上这个颜色,求最后每个矩阵会有多少种颜色。


解:

我们可以先把矩阵拆成上下两条水平线段,然后离线将染色与线段横坐标离散化,以纵坐标将矩阵将线段与染色一起处理。

维护一棵线段树,对于一个矩阵的下方线段加入,直接在线段树对应节点将其入栈,对于上方线段,直接在对应节点弹出栈顶即可,因为矩阵无相交,所以栈顶一定是需要弹出的线段。

对于染色操作,我们找到线段树对应的栈顶元素,用 set 记录一下。

由于若将大矩阵覆盖小矩阵看成大矩阵为小矩阵的父亲节点,那么不难发现这是棵森林,所以我们对于染色的节点,它们父亲也需要染这个颜色,这个就遍历一遍树用启发式合并即可。

时间复杂度:\(O(n\times \log_2^2(n))\)。

#include<iostream>
#include<cstdio>
#include<vector>
#include<set> 
#include<stack>
#include<algorithm>
using namespace std;
const int N=8e4+5;
int n,m,cnt,ans[N],cnp,ru[N],len1[12*N],len2[12*N];
set<int>num[N];
vector<int>b[N];
struct node
{
	int name,x,y,h,k,flag;
}a[3*N];
struct node2
{
	int name,data;
	bool flag;
}t[3*N];
int cmp(node2 fi,node2 se)
{
	return fi.data<se.data;
}
int cmp2(node fi,node se)
{
	if(fi.h==se.h)return fi.flag<se.flag;
	return fi.h<se.h;
}
vector<int>f[12*N];
inline int ls(int x)
{
	return x<<1;
}
inline int rs(int x)
{
	return x<<1|1;
}
void update(int x,int l,int r,int nl,int nr,int id,bool flag)
{
	if(l>=nl&&r<=nr)
	{
		if(flag==0)
		{
			if(len1[x]==len2[x])f[x].push_back(id),len1[x]++,len2[x]++;
			else f[x][len1[x]++]=id;
		}
		else len1[x]--;
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=nl)update(ls(x),l,mid,nl,nr,id,flag);
	if(mid<nr)update(rs(x),mid+1,r,nl,nr,id,flag);
}
int search(int x,int l,int r,int nl)
{
	int ans=0;
	if(len1[x]>0)ans=f[x][len1[x]-1];
	if(l==r)return ans;
	int mid=(l+r)>>1;
	
	if(mid>=nl)ans=max(ans,search(ls(x),l,mid,nl));
	else ans=max(ans,search(rs(x),mid+1,r,nl));
	return ans;
}
void dfs(int x)
{
	int len=b[x].size();
	for(int i=0;i<len;i++)
	{
		dfs(b[x][i]);
		if(num[b[x][i]].size()>num[x].size())swap(num[x],num[b[x][i]]);
		for(set<int>::iterator j=num[b[x][i]].begin();j!=num[b[x][i]].end();j++)num[x].insert(*j);
	}
	ans[x]=num[x].size();
}
int main()
{
	//freopen("plahteplahte.in","r",stdin);
	//freopen("Plahte.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&a[i*2-1].x,&a[i*2-1].h,&a[i*2].y,&a[i*2].h);
		a[i*2-1].name=a[i*2].name=i;
		a[i*2-1].y=a[i*2].y;
		a[i*2].x=a[i*2-1].x;
		t[++cnp]={i,a[i*2-1].x,0};
		t[++cnp]={i,a[i*2-1].y,1};
		a[i*2-1].flag=0;
		a[i*2].flag=2;
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a[i+2*n].x,&a[i+2*n].h,&a[i+2*n].k);
		t[++cnp]={i+2*n,a[i+2*n].x,0};
		a[i+2*n].flag=1;
	}
	sort(t+1,t+1+cnp,cmp);
	t[0].data=-1;
	for(int i=1;i<=cnp;i++)
	{
		if(t[i].data!=t[i-1].data)cnt++;
		if(t[i].flag==0)
		{
			if(t[i].name<=2*n)a[t[i].name*2-1].x=a[t[i].name*2].x=cnt;
			else a[t[i].name].x=cnt;
		}
		else a[t[i].name*2-1].y=a[t[i].name*2].y=cnt;
	}
	sort(a+1,a+1+2*n+m,cmp2);
	for(int i=1;i<=2*n+m;i++)
	{
		//cout<<a[i].name<<" "<<a[i].x<<" "<<a[i].y<<" "<<a[i].flag<<endl;
		if(a[i].flag==0)
		{
			int x=search(1,1,cnt,a[i].x);
			if(x)b[a[x].name].push_back(a[i].name),ru[a[i].name]++;
			update(1,1,cnt,a[i].x,a[i].y,i,0);
		}
		else if(a[i].flag==1)
		{
			int x=search(1,1,cnt,a[i].x);
			if(x)num[a[x].name].insert(a[i].k);
		}
		else update(1,1,cnt,a[i].x,a[i].y,i,1);
	}
	for(int i=1;i<=n;i++)if(!ru[i])dfs(i);
	for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
	return 0;
}

标签:int,题解,线段,矩阵,COCI2017,num,ans,P4416,include
From: https://www.cnblogs.com/gmtfff/p/p4416.html

相关文章

  • CF1034A Enlarge GCD 题解
    题意简述:删去最少的数使所有的数的\(\text{gcd}\)增加。解:先对每个数除以所有数的\(\text{gcd}\),然后剩下的需要找到所有数的质因子,找到一个最多的序列中数拥有的质......
  • CF436E Cardboard Box 题解
    一道经典的反悔贪心题。考虑每次选择使总星数加一,那么总共有四种情况。一、对于一关没有星,选一颗星,代价为\(a_i\)。二、对于一关有一颗星,再选一颗星,代价为\(b_i-a_i\)......
  • CF204E Little Elephant and Strings 题解
    由于是多个串,还与每个子串的信息有关,很容易想到用SA或广义SAM。这里选择用SA。首先先把字符串转化为数组,连接起来,中间用一些不会出现的数。处理出后缀数组与\(height......
  • P5842 [SCOI2012]Blinker 的仰慕者 题解
    题意简述:求\([l,r]\)内各个数位积为\(k\)的数的和。解:在看题解前,请先确认自己是否精通了数位dp模板题,否则会很难理解。对于朴素的数位dp,我们可以记录\(f_{i,j......
  • P5416 [CTSC2016]时空旅行 题解
    首先题中的\(y,z\)好像没啥用……首先对于每一次询问,要求\(\min((x_0-x_i)^2+c_i)\)设\((x_0-x_i)^2+c_i=ans\)。那么\(x_0^2+x_i^2-2x_0x_i+c_i=ans\)所以\(x_0......
  • P7862 [COCI2015-2016#2] DRŽAVA 题解
    适当进行骗分是真的有用。\(40pts\):对于每两个点建立一条边,然后在贪心每次求最小边,在期间进行01背包即可,01背包用于处理模数。设\(dp_{i,j}\)代表以\(i\)为编号的一......
  • P1379 八数码难题 题解
    IDA*练习题由于题目问最小步数,很好想到可以用迭代式加深搜索,或是广搜,这里用的是深搜。枚举每次搜索的深度,也就是移动的步数,然后正常深搜,若达到目标解,返回\(\text{ture}......
  • P3213 [HNOI2011]勾股定理 题解
    据说是NP问题。很明显我们要先预处理出来勾股数对。但由于数过于大,所以常规的枚举是解决不了问题的。但也貌似没有什么很好的办法可以立马找到一个数的勾股数对。所以......
  • P3989 [SHOI2013]阶乘字符串 题解
    由于一些不可抗拒的原因,\(n\ge22\)无解。那么只用考虑\(n\le21\)的情况即可。由于\(n\)的范围缩小,导致状压又可以重新使用,所以考虑状压。设\(f_i\)为\(i\)中......
  • AT655 玉座の間 题解
    首先我们要学习一下费用流。费用流是什么呢,可以理解为边带权值的网络流。那么最小费用最大流,是指在满足最大流的情况下的最小费用。那么我们就要实现这个过程。首先对......