题目链接: https://www.luogu.org/problemnew/show/T21778
有 n(1≤n≤ 1e5) 个小朋友,过年了,要发放 m(1≤m≤ 1e5) 次礼物。
每次发放,会给出三个参数 l,r,k(1≤l≤r≤n,1≤k≤ 1e5) ,表示给区间 [l,r] 内的小朋友都发一个礼物 k 。
所有礼物发放完成后,对于每一个小朋友,回答他接受的礼物中,出现次数最多的礼物是什么。如果有多个,输出编号最小的那个;如果不存在,输出 -1 。
算法:扫描线、差分、线段树
将每一个l~r发礼物的操作差分为位置l数量+1,位置r+1数量-1
线段树维护一段礼物的最大数量和编号
从1至n循环,每次将这个位置的操作加入线段树后根节点的数据即为答案
代码如下:
#include<bits/stdc++.h>
#define sz 100010
using namespace std;
int n,m;
vector<int>vec[sz];//vec[i]存储第i个小朋友的差分操作
pair<int,int> a[10001010];//a存储线段树,first:子树中礼物最大数量 second:子树中数量最多的礼物中最小的编号
void build(int rt,int l,int r)//建树
{
a[rt].second=l;
if (l<r)
{
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
}
void push_up(int rt)//更新rt节点
{
a[rt].first=max(a[rt<<1].first,a[rt<<1|1].first);
if (a[rt<<1].first>=a[rt<<1|1].first) a[rt].second=a[rt<<1].second;
else a[rt].second=a[rt<<1|1].second;
}
void add(int rt,int l,int r,int pos,int k)//第pos个礼物数量+=k
{
if (l==r) {a[rt].first+=k;return;}
int mid=(l+r)>>1;
if (pos<=mid) add(rt<<1,l,mid,pos,k);
else add(rt<<1|1,mid+1,r,pos,k);
push_up(rt);
}
int main()
{
scanf("%d %d",&n,&m);
for (int i=1;i<=m;i++)
{
int l,r,x;
scanf("%d %d %d",&l,&r,&x);
vec[l].push_back(x);
vec[r+1].push_back(-x);
//差分 正数表示+1,负数表示-1
}
build(1,1,sz);
for (int i=1;i<=n;i++)
{
for (int j=0;j<vec[i].size();j++)
{
int cur=vec[i][j];
if (cur>0) add(1,1,sz,cur,1);
else add(1,1,sz,-cur,-1);
}
pair<int,int>ans=a[1];//a[1]是根节点
if (ans.first==0) ans.second=-1;//如果没有礼物输出-1
printf("%d\n",ans.second);
}
}
个人认为这是Day1三道题中最简单的题了