CFdiv3-Multi-Colored Segments-(线段树+multiset)

F

题意:
就是给你n个线段,每个线段有个a,b,c,代表左端点,右端点,线段的颜色。现在问你每个线段距离其他颜色的线段中最小的距离是多少。如果有点交叉那么距离就是0。

思考:

  1. 当时看到距离那么肯定是要维护两个set,分别是存左端点和右端点的。然后发现距离必须是两个线段的颜色不一样,那么我就可以对每种颜色分类处理,先对某个颜色的线段处理,那么从set里删去这个颜色的东西,处理完之后,再放进去。复杂度也就是2×n×log(n)。
  2. 但是这样只能处理线段相离或者相交的,不能处理线段包含关系的。也就是对于[a,b]没法求别人包括它的情况。所以我就想了,能不能用线段树,对于某个点每有一个不同的颜色出现,那么这个点就加1。但是如果直接累加的话,相同颜色的线段之间还会重复,所以可能会加多,所以先用个经典的排序,求连续的区间就可以了。那么把这段连续的区间在线段树中加1就可以了。
  3. 但是我发现,线段的范围很大,1e9的范围。所以直接累加的话,线段树建立不了那么大,那么我就想了,感觉可以离散化,因为对于包含的那种关系,确实也是看点的大小,也就是离散化后包含关系不会改变。那么离散化后去线段树累加就可以了,复杂度n×log(n)。
  4. 那么最后对于这个线段[a,b],怎么判断是否这个里面有别的颜色的点呢?那么就是如果[a,b]之间某个点的值>=2就可以了,那我还要查询这段区间的最大值?其实不用,只要知道[a,b]这段区间的总和就好了,只要sum[a,b]>b-a+1,那么肯定有一个点是>=2的。
  5. 那么到此这个题目就结束了,感觉我真腻害,嘿嘿。其实就是把题目不要感觉太难,能有想法就想想,想想就出来了一般。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;
const int mod = 1e9+7,inf = 1e9;
const int N = 4e5+10,M = 2010;

int T,n,m,k;
PII va[N];
int anw[N];

vector<int > vv;
multiset<int > sl,sr;
vector<pair<PII,int> > v[N];

struct seg_sum{
	#define node_l node<<1
	#define node_r node<<1|1
	struct node{
		int L,R;
		int sum;
		int laz;
	}t[4*N];
	void pushup(int node)
	{
		t[node].sum = t[node_l].sum+t[node_r].sum;
	}
	void pushdown(int node)
	{
		int laz = t[node].laz;
		if(laz)
		{
			t[node_l].laz += laz;
			t[node_l].sum += (t[node_l].R-t[node_l].L+1)*laz;
			t[node_r].laz += laz;
			t[node_r].sum += (t[node_r].R-t[node_r].L+1)*laz;
			t[node].laz = 0;
		}
	}
	void build(int node,int l,int r)
	{
		t[node].laz = t[node].sum = 0;
		t[node].L = l,t[node].R = r;
		if(l==r)
		{
			return ;
		}
		int mid = (l+r)>>1;
		build(node_l,l,mid);build(node_r,mid+1,r);
		pushup(node);
	}
	void update(int node,int l,int r,int value)
	{
		if(t[node].L>=l&&t[node].R<=r)
		{
			t[node].sum += (t[node].R-t[node].L+1)*value;
			t[node].laz += value;
			return ;
		}
		pushdown(node);
		int mid = (t[node].L+t[node].R)>>1;
		if(r<=mid) update(node_l,l,r,value);
		else if(l>mid) update(node_r,l,r,value);
		else update(node_l,l,mid,value),update(node_r,mid+1,r,value);
		pushup(node);
	}
	int query(int node,int l,int r)
	{
		if(t[node].L>=l&&t[node].R<=r) return t[node].sum;
		pushdown(node);
		int mid = (t[node].L+t[node].R)>>1;
		if(r<=mid) return query(node_l,l,r);
		else if(l>mid) return query(node_r,l,r);
		else return query(node_l,l,mid)+query(node_r,mid+1,r);
		pushup(node);
	}
}tsum;

void solve(int x)
{
	for(auto t:v[x]) sl.erase(sl.find(t.fi.fi));
	for(auto t:v[x]) sr.erase(sr.find(t.fi.se));
	for(auto t:v[x])
	{
		auto idx = sl.lower_bound(t.fi.fi);
		int minn = inf;
		if(idx!=sl.end()) minn = min(minn,*idx-t.fi.se);
		idx = sr.upper_bound(t.fi.se);
		if(idx!=sr.begin())
		{
			idx--;
			minn = min(minn,t.fi.fi-*idx);
		}
		anw[t.se] = max(0,minn);
	}
	for(auto t:v[x]) sl.insert(t.fi.fi);
	for(auto t:v[x]) sr.insert(t.fi.se);
}

bool cmp(pair<PII,int> A,pair<PII,int> B)
{
	if(A.fi.fi!=B.fi.fi) return A.fi.fi<B.fi.fi;
	return A.fi.se<B.fi.se;
}

int get(int x)
{
	return lower_bound(vv.begin(),vv.end(),x)-vv.begin()+1;
}

void futin(int x)
{
	if(v[x].size()==0) return ;
	for(auto &t:v[x])
	{
		t.fi.fi = get(t.fi.fi);
		t.fi.se = get(t.fi.se);
	}
	sort(v[x].begin(),v[x].end());
	int st = v[x][0].fi.fi,ed = v[x][0].fi.se;
	for(auto t:v[x])
	{
		int l = t.fi.fi,r = t.fi.se;
		if(l<=ed+1) ed = max(ed,r);
		else
		{
			tsum.update(1,st,ed,1);
			st = l,ed = r;
		}
	}
	tsum.update(1,st,ed,1);
}

void init()
{
	vv.clear();
	sl.clear();sr.clear();
	for(int i=0;i<=n;i++)
	{
		anw[i] = inf;
		v[i].clear();
	}
}

signed main()
{
	IOS;
	cin>>T;
	while(T--)
	{
		cin>>n;
		init();
		for(int i=1;i<=n;i++)
		{
			int a,b,c;
			cin>>a>>b>>c;
			v[c].pb({{a,b},i});
			sl.insert(a);sr.insert(b);
			vv.pb(a);vv.pb(b);
		}
		sort(vv.begin(),vv.end());
		vv.erase(unique(vv.begin(),vv.end()),vv.end());
		tsum.build(1,1,vv.size());
		for(int i=1;i<=n;i++) solve(i);
		for(int i=1;i<=n;i++) futin(i);
		for(int i=1;i<=n;i++)
		{
			for(auto t:v[i])
			{
				int sum = tsum.query(1,t.fi.fi,t.fi.se);
				if(sum>t.fi.se-t.fi.fi+1) anw[t.se] = 0;
			}
		}
		for(int i=1;i<=n;i++) cout<<anw[i]<<" ";
		cout<<"\n";
	}
	return 0;
}

总结:
多多思考,不用觉得题目难,思考思考思路,一个不行再换,这样可以积累许多以后可能用到的操作。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值