C. Paint(单调栈,上海12月月赛)

Linking


题意:

给定长度为 n 的栅栏,需要将这些栅栏染色,初始每个栅栏的颜色都为0。
两种操作:
1 x y:将最后的 x 个栅栏染成颜色y;
2:输出这 n 个栅栏的颜色种类数。
在这里插入图片描述

思路:

像不像线段树?一开始就奔着这个去的,到最后都没写出来。。
线段树的染色问题是:可以染色和询问任意区间的栅栏,而染色颜色的种类不超过30(或60)种。
这样便可以用二进制来表示出每个区间的颜色种类数,两个区间颜色种类数合并的话只需要将两个二进制数取或。很妙的思路!
但这题的特殊性在于:每次染色都将后面的所有位置都染上同种颜色,而每次询问都输出整个区间的颜色种类。并且颜色种类不止有60种。

正解:
因为每次染色对于给定的 x,x = n-x+1,这样就是从第 x 个位置到最后一个位置都染成同一颜色 y。那么 x 位置后面的所有位置之前的颜色就作废了,就可以把那个颜色出现的次数-1。如果-1之后出现的次数为0了,说明就没有这个颜色了,整个区间的颜色种类数cnt -= 1。
对于每次染色,都要把该位置后面的所有位置,即之前染色的比当前大的所有位置都消除掉。所以可以开一个栈:

  • 每次染色都把大于该位置的所有位置出栈,将其颜色出现的次数–。如果颜色出现次数变为0了,那么区间颜色种类数cnt–。
  • 同时当前染色的颜色出现的次数++,如果这个颜色是第一次出现,那么区间颜色种类数cnt++。
  • 当前位置压栈。

由于每个位置只入栈出栈一次,所以整个复杂度为O(m)。

Code:
map<int,int> mp;
const int N = 500010, mod = 1e9+7;
int T, n, m;
int a[N];
struct node{
	int pos,kind;
};

int main(){
	cin>>n>>m;
	
	stack<node> stk;
	stk.push({1,0});
	mp[0]++;
	
	int cnt=1;
	
	while(m--)
	{
		int k;cin>>k;
		if(k==1)
		{
			int x,y;cin>>x>>y;
			x=n-x+1;
			while(stk.size()&&stk.top().pos>=x)
			{
				mp[stk.top().kind]--;
				if(!mp[stk.top().kind]) cnt--;
				stk.pop();
			}
			stk.push({x,y}); 
			if(!mp[y]) cnt++;
			mp[y]++;
		}
		else cout<<cnt<<endl;
	}
	
	return 0;
}

挺好的一道题,不要困在线段树的思路里!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值