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;
}