P2564 [SCOI2009] 生日礼物 题解
CleverRaccoon · · 题解
前言
我是不是好久没来写题解了。题解区无 STL 大法,特此申请添加。
题目简述
有一个很长的序列,每个位置上有一种的颜色(同一位置可能有多个颜色)。取序列上长度为
前置知识
STL 里的 containers 相信都不陌生。介绍一下用到的。
map
含义是,对于每一个位置,映射一个数组记录当前位置上所有的元素种类。单次操作时间复杂度约
map<int,vector<int>> e;
unordered_map
含义同 map,区别在于该映射容器底层实现为哈希表,故操作时间复杂度不带 log,无需排序则使用。代码中为记录每个颜色种类的出现次数。
unordered_map<int,int> omo;
deque
双端队列,可双端增删,无需多言。但题外话,注意若开 deque 是会 MLE 的。
deque<int> q;
思路
题目的输入给的很乱。我们要学会归纳总结信息。其实此题与 P1638 逛画展 非常类似。但问题就在于要先处理好输入信息。
step 1 转化信息
总的来说,即将输入数据处理为按照位置信息升序排序的数组,借助 map 的自动排序,也相当于一个离散化过程。即可转化为类似逛画展的输入数组。
具体地,我们可以用一个 map 映射每个位置对应的几个种类,具体见前置知识部分。由于 map 能够自动按照 key 排序,所以遍历一遍 map,每次遍历取出每个位置对应的种类,并直接加入数组
cin>>n>>k;
for(int i=1;i<=k;i++){
cin>>t[i];
for(int j=1;j<=t[i];j++){
int tmp;cin>>tmp;
e[tmp].push_back(i);
}
}
for(auto &p:e){
int m=p.first;
vector<int> v=p.second;
for(auto &y:v)c[++cnt]={y,m};
}
step 2 处理
其实之后的处理工作就和逛画展很一样了。这里提供逛画展的一种 STL 写法。
用 unordered_map(具体前置知识提到过)记录每个种类出现的个数,用 deque 记录当前选中的部分序列的元素(即为了记录删除头部哪一个元素)。
想象一个滑动窗口,由于已经得到位置升序的有序数组了,所以可以从
对于每次遍历,将当前的种类加入 unordered_map 和 deque,并将当前的位置向后推移,直到 unordered_map 的长度(即种类数)达到
int st;
if(!q.empty())st=q.front();
else st=1;
while(i<=n&&omo.size()<k){
q.push_back(i);
if(omo.count(c[i].cs))omo[c[i].cs]++;
else omo.insert({c[i].cs,1});
++i;
}
--i;
ans=min(ans,c[i].id-c[st].id);
之后每一次都需要向后滑动,才能减少种类,并找到新的合适的位置,增加种类,实现答案最小化。滑动的时候就需要删除队首的元素。如果元素数量为
if(!q.empty()){
omo[c[q.front()].cs]--;
if(!omo[c[q.front()].cs])omo.erase(c[q.front()].cs);
q.pop_front();
}
实现
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,k,t[N],cnt,ans=INT_MAX;
map<int,vector<int>> e;
struct node{
int cs;// 种类
int id;// 位置
}c[N];
int main(){
cin.tie(nullptr)->sync_with_stdio(false);
cin>>n>>k;
for(int i=1;i<=k;i++){
cin>>t[i];
for(int j=1;j<=t[i];j++){
int tmp;cin>>tmp;
e[tmp].push_back(i);// 位置映射种类
}
}
for(auto &p:e){
int m=p.first;
vector<int> v=p.second;
for(auto &y:v)c[++cnt]={y,m};// 记录到新数组中
}
deque<int> q;
unordered_map<int,int> omo;
for(int i=1;i<=n;i++){
if(!q.empty()){// 滑动
omo[c[q.front()].cs]--;
if(!omo[c[q.front()].cs])omo.erase(c[q.front()].cs);
q.pop_front();
}
int st;
if(!q.empty())st=q.front();
else st=1;
while(i<=n&&omo.size()<k){// 寻找一组种类达到 k 的可行解
q.push_back(i);
if(omo.count(c[i].cs))omo[c[i].cs]++;
else omo.insert({c[i].cs,1});
++i;
}
--i;
ans=min(ans,c[i].id-c[st].id);// 取更小的答案
}
cout<<ans;
return 0;
}
后记
泥萌给她送过生日礼物吗 QwQ。
我没有。