codeforces1208E Let Them Slide

http://codeforces.com/problemset/problem/1208/E

今天我终于认识到把多个区间的问题变成一个区间用打标记去线段树处理是不可行的,上次写偶像的树链合并也是想用这种方法,结果也8太行。

看了题解,这题有更简单的做法。我们对每一行开一个multiset,然后按列从左枚举到右,multiset[i]维护第i行在当前列中有哪些数字是可以放的,那么我们取最大的那个就行。

对于每个数字,我们计算出它能够放的区间为[l,r]那么我们在in[l]插入这个数字和他所在的行,out[r]也插入。

那么枚举每一列的时候,我们先in[i]全部插入,每次插入就减去上次的最大,然后插入,再加上当前的最大。

这样ans[i]就确认了,但是ans[i+1]就要考虑是否要减去out[i]中的这些值。

因为数字个数少于1e6,那么对multiset的插入和删除为k*1e6次,复杂度为O(1e6 log(1e6))

#include<bits/stdc++.h>
#define maxl 1000010
using namespace std;
 
int n,w;
typedef pair<long long,int> p;
multiset <long long> mx[maxl];
vector <p> in[maxl],out[maxl];
long long ans[maxl];
 
inline void prework()
{
	scanf("%d%d",&n,&w);
	int len,d;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&len);
		if(len<w)
		{
			int l=1,r=w-len;
			in[l].push_back(make_pair(0,i));
			out[r].push_back(make_pair(0,i));
			l=len+1,r=w;
			in[l].push_back(make_pair(0,i));
			out[r].push_back(make_pair(0,i)); 
		}
		for(int j=1;j<=len;j++)
		{
			scanf("%d",&d);
			int l=j,r=w-len+j;
			in[l].push_back(make_pair(d,i));
			out[r].push_back(make_pair(d,i));
		}
	}
}
 
inline  void mainwork()
{
	p d;int l;
	for(int i=1;i<=w;i++)
	{
		l=in[i].size();
		for(int j=0;j<l;j++)
		{
			d=in[i][j];
			if(mx[d.second].begin()!=mx[d.second].end())
				ans[i]-=*mx[d.second].rbegin();
			mx[d.second].insert(d.first);
			ans[i]+=*mx[d.second].rbegin();
		}
		ans[i+1]=ans[i];
		l=out[i].size();
		for(int j=0;j<l;j++)
		{
			d=out[i][j];
			ans[i+1]-=*mx[d.second].rbegin();
			mx[d.second].erase(mx[d.second].find(d.first));
			if(mx[d.second].begin()!=mx[d.second].end())
				ans[i+1]+=*mx[d.second].rbegin();
		}
	}
}
 
inline void print()
{
	for(int i=1;i<=w;i++)
		printf("%lld ",ans[i]);
}
 
int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值