题解:CF2059E2 Stop Gaming (Hard Version)
先讲一下题意,赛时一直在写假题。
给你
- 选择某一行,在它的头部插入任意一个数,其它数后推一格,并删去最后一行最后一个数。
下面是一个例子(选择第
求使
最小操作次数等于
因为每次删数都是删去矩阵内最后一个数,所以留下的
再看看
如何判断
如果
假设它前面来自
假如满足上面的条件,一直插入最靠后且满足条件的数就是一个方案,即这个数可以来自插入。发现这是个充要条件。
于是直接将
什么?你说复杂度不对?
别忘了,
E2 就是输出这个“一直插入最靠后且满足条件的数”的过程,预处理出每个数插入前提是前面插入多少个数,拍到线段树上模拟就好。
E1
void R()
{
int n,m;
cin>>n>>m;
int N=n*m;
vector<int> a(N),b(N),c;
for (int &x:a) cin>>x;
for (int &x:b) cin>>x;
auto back=[&](auto &self,int x)->void
{
while (x-c.size()<x%m)
{
int t=c.back();
c.pop_back();
self(self,t);
}
};
for (int i=0;i<N;i++)
{
if (b[i]==a[c.size()])
c.push_back(i);
else back(back,i);
}
cout<<N-c.size()<<'\n';
return;
}
E2
略去了区间
void R()
{
int n,m;
cin>>n>>m;
int N=n*m;
vector<int> a(N),b(N),c,id(2*N+1),vis(2*N+1);
SGT<Info,Tag> sgt(N);
for (int i=0;i<N;i++)
{
cin>>a[i];
id[a[i]]=i;
}
for (int &x:b) cin>>x;
auto back=[&](auto &self,int x)->void
{
while (x-c.size()<x%m)
{
int t=c.back();
c.pop_back();
self(self,t);
}
};
for (int i=0;i<N;i++)
{
if (b[i]==a[c.size()])
c.push_back(i);
else back(back,i);
}
for (int p:c) vis[b[p]]=1;
for (int i=0;i<N;i++)
if (!vis[b[i]])
{
int pre=lower_bound(c.begin(),c.end(),i)-c.begin();
sgt.modify(i,{(m-pre%m)%m});
}
int ans=N-c.size();
cout<<ans<<'\n';
for (int i=0;i<ans;i++)
{
auto pred=[&](Info x)->bool
{
return x.x<=0;
};
int pb=sgt.findLast(0,N,pred);
int pre=lower_bound(c.begin(),c.end(),pb)-c.begin();
cout<<(pre+(m-pre%m)%m)/m+1<<' '<<b[pb]<<'\n';
sgt.rangeApply(pb,N,{-1});
sgt.modify(pb,{inf});
}
return;
}