Solution for ABC478D
这道题就是说,我们有 \(n\) 个集合,然后一共有 \(q\) 次操作,每次操作给一个区间 \([l,r]\) 和一个整数 \(x\),想要将编号在 \([l,r]\) 区间内的集合全部加入 \(x\) 这个元素,问最后每个集合中有多少个元素(去重后)。
那么一个暴力解法就比较显然,直接模拟这 \(q\) 次询问,维护 \(n\) 个集合,时间复杂度 \(O(nq)\),空间复杂度 \(O(nq)\),需要优化。
这个暴力解法,是通过集合去看元素的,是使用元素进行操作,那么我们为什么不换一个角度,通过元素来看集合呢?
每一次 \(x\) 元素都要覆盖整个 \([l,r]\) 区间,并且面临去重,并且还是动态调整,那么我们为何不思考,通过 \(x\) 来直接映射到区间呢?
具体的,对于每一个元素,他们都有覆盖若干个区间,由于集合的互异性,所以每一个集合中最多只有一个这个元素。
那么我们可以考虑“批量操作”:将这个元素覆盖的区间进行“打包”,直接合并区间,直接进行覆盖这个合并后的区间,那么就不存在“重复”的问题。由于我们需要统计这个元素覆盖的所有区间,那么我们需要进行离线处理。
由于我们需要对于这个覆盖的连续区间进行处理,并且我们不关心具体元素,并且不存在去重的问题,那么直接计数就行了。
说到区间计数,也就是区间加,那么就有两个方案:差分和线段树。(由于作者太弱了并且线段树有点长并且比赛时间不够了所以作者只写了差分的写法)
那么最后求一下差分数组的前缀和就是每个集合的实际大小了。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,q,l[N],r[N],x[N],diff[N],nu[N],sum[N];
struct node{
int l,r;
bool operator<(const node& b)const{
if(l!=b.l) return l<b.l;
else return r<b.r;
}
};
bool cmp(const node& a,const node& b){
if(a.l!=b.l) return a.l<b.l;
else return a.r<b.r;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>q;
map<int,vector<node>> mp;
set<int> st;
int id=0;
for(int i=1;i<=q;i++){
cin>>l[i]>>r[i]>>x[i];
mp[x[i]].push_back({l[i],r[i]});
if(st.find(x[i])==st.end()){
id++;
nu[id]=x[i];
st.insert(x[i]);
}
}
vector<node> tmp;
for(int i=1;i<=id;i++){
tmp=mp[nu[i]];
sort(tmp.begin(),tmp.end(),cmp);
vector<node> now;
for(node v:tmp){
if(now.empty()||v.l>now.back().r+1) now.push_back(v);
else now.back().r=max(now.back().r,v.r);
}
for(node v:now){
diff[v.l]++;
diff[v.r+1]--;
//cout<<v.l<<' '<<v.r<<'\n';
}
}
//for(int i=1;i<=n;i++) cout<<diff[i]<<' ';
//cout<<'\n';
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+diff[i];
for(int i=1;i<=n;i++) cout<<sum[i]<<' ';
return 0;
}

浙公网安备 33010602011771号