Codeforces 题目传送门 & 洛谷题目传送门

首先考虑 \(p>50\) 的时候怎么处理,也就是求一个区间的绝对众数。我们知道众数这个东西是不能用线段树直接维护的,因为对于区间 \([l,r]\) 和 \(x\in [l,r)\),区间 \([l,r]\) 的众数不一定是 \([l,x]\) 的众数或 \([x+1,r]\) 的众数。

不过此题有一个特殊之处,就是我们要求区间的绝对众数而不是区间的众数。这时候就可以用线段树维护了,因为区间 \([l,r]\) 的绝对众数一定至少是区间 \([l,x],[x+1,r]\) 中某个区间的绝对众数(提示:反证法)。

于是我们考虑这样一个思路,对于 \([l,r]\) 中的所有数组成一个可重集 \(S\),每次选择两个数 \(x,y\in S\) 且 \(x\ne y\) 并将 \(x,y\) 从 \(S\) 中删除,直到 \(S=\varnothing\) 或 \(S\) 中所有数都相同为止。不难发现在任意时刻,原区间中的绝对众数还是 \(S\) 中的绝对众数,因为对于 \([l,r]\) 中的绝对众数 \(z\),我们设其出现次数为 \(c\),若删去的两数满足 \(x\ne z\land y\ne z\),那么删去 \(x,y\) 后还剩 \(n-2\) 个数,其中 \(z\) 出现的次数为 \(c\),\(c>\lfloor\dfrac{n}{2}\rfloor>\lfloor\dfrac{n-2}{2}\rfloor\);若删去的两数满足 \(x=z\lor y=z\),那么删去 \(x,y\) 后 \(z\) 出现的次数为 \(c-1\),由 \(c>\lfloor\dfrac{n}{2}\rfloor\) 知 \(c-1>\lfloor\dfrac{n-2}{2}\rfloor\)。我们建一棵线段树,区间 \([l,r]\) 表示的节点上维护两个值 \(x,y\),\(x\) 为将 \([l,r]\) 中所有数压入 \(S\) 后对 \(S\) 进行上文所说的操作后 \(S\) 中剩余的数,如果最终 \(S=\varnothing\) 则该值为 \(0\),\(y\) 是删完后 \(x\) 的出现次数,合并两个区间 \([l,r]\) 时就将两个子区间的 \(y\) 值作比较,并令当前区间的 \(x\) 为两个子区间的 \(y\) 值较大的 \(x\),\(y\) 值为两个子区间的 \(y\) 值相减即可。

到这里你可能会有疑惑,那就是任意时刻 \(x\) 为 \(S\) 中的绝对众数并不是 \(x\) 为原区间的绝对众数的充要条件,它仅仅只是必要条件。也就是说有可能出现最终留下的数不是原区间的绝对众数。但是注意到题目中有个奇奇怪怪的条件,叫做“输出的数中可以包含错误的数,但正确的数必须包含在输出中,并且输出的数不能超过 \(\lfloor\dfrac{100}{p}\rfloor\)”,在上述算法中我们最多输出一个数,并且可以保证若 \([l,r]\) 存在绝对众数,那么其绝对众数一定被输出,故这样是符合题意。

接下来考虑原题。我们记 \(k=\lfloor\dfrac{100}{p}\rfloor\)。由于 \(p\geq 20\),一定有 \(k\leq 5\),也就是说我们最多输出 \(5\) 个数。我们还是考虑之前的算法,只不过把“每次删除 \(2\) 个数”改为“每次删除 \(k+1\) 个数”,然后用一个 std::vector<pair<int,int> > 维护该区间中的数执行上述操作后剩余的数,以及它们的出现次数。合并时候就暴力合并左右子区间的 vector,取出现次数最大的 \(k\) 个数即可。借鉴上面的分析可知输出的数仍可能包含错误的数,但正确的数一定被包含在答案中。

时间复杂度 \(nk^2\log n\),其中 \(k_{\max}=5\)

不过似乎 std::vector<pair<int,int> > 常数有亿点点大?或许是 wtcl 罢,用 std::vector<pair<int,int> > 实现的代码 TLE 50 了,故这里是用结构体实现的代码:

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1.5e5;
const int INF=0x3f3f3f3f;
int n,qu,p,a[MAXN+5];
struct seq{
int cnt,x[8],y[8];
seq(){cnt=0;fill0(x);fill0(y);}
};
struct node{int l,r,lz;seq v;} s[MAXN*4+5];
seq merge(seq a,seq b){
for(int i=1;i<=b.cnt;i++){
bool ok=0;
for(int j=1;j<=a.cnt;j++){
if(a.x[j]==b.x[i]){
a.y[j]+=b.y[i];
ok=1;break;
}
} if(ok) continue;
a.x[++a.cnt]=b.x[i];a.y[a.cnt]=b.y[i];int mn=INF;
if(a.cnt<=p) continue;
for(int j=1;j<=a.cnt;j++) chkmin(mn,a.y[j]);
seq c;
for(int j=1;j<=a.cnt;j++) if(a.y[j]-mn)
c.x[++c.cnt]=a.x[j],c.y[c.cnt]=a.y[j]-mn;
a=c;
} return a;
}
void pushup(int k){s[k].v=merge(s[k<<1].v,s[k<<1|1].v);}
void build(int k=1,int l=1,int r=n){
s[k].l=l;s[k].r=r;if(l==r){s[k].v.cnt=1;s[k].v.x[1]=a[l];s[k].v.y[1]=1;return;}
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void pushdown(int k){
if(s[k].lz){
s[k<<1].v.cnt=1;s[k<<1].v.x[1]=s[k].lz;
s[k<<1].v.y[1]=s[k<<1].r-s[k<<1].l+1;
s[k<<1].lz=s[k].lz;
s[k<<1|1].v.cnt=1;s[k<<1|1].v.x[1]=s[k].lz;
s[k<<1|1].v.y[1]=s[k<<1|1].r-s[k<<1|1].l+1;
s[k<<1|1].lz=s[k].lz;
s[k].lz=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
s[k].v.cnt=1;s[k].v.x[1]=x;
s[k].v.y[1]=s[k].r-s[k].l+1;
s[k].lz=x;return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
pushup(k);
}
seq query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].v;
int mid=s[k].l+s[k].r>>1;pushdown(k);
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return merge(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
int main(){
scanf("%d%d%d",&n,&qu,&p);p=100/p;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);build();
while(qu--){
int opt;scanf("%d",&opt);
if(opt==1){int l,r,x;scanf("%d%d%d",&l,&r,&x);modify(1,l,r,x);}
else{
int l,r;scanf("%d%d",&l,&r);seq ret=query(1,l,r);
printf("%d ",ret.cnt);for(int i=1;i<=ret.cnt;i++) printf("%d ",ret.x[i]);
printf("\n");
}
}
return 0;
}

Codeforces 643G - Choosing Ads(线段树)的更多相关文章

  1. Buses and People CodeForces 160E 三维偏序+线段树

    Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...

  2. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  3. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  4. [Codeforces 1199D]Welfare State(线段树)

    [Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...

  5. [Codeforces 316E3]Summer Homework(线段树+斐波那契数列)

    [Codeforces 316E3]Summer Homework(线段树+斐波那契数列) 顺便安利一下这个博客,给了我很大启发(https://gaisaiyuno.github.io/) 题面 有 ...

  6. Codeforces Gym 100231B Intervals 线段树+二分+贪心

    Intervals 题目连接: http://codeforces.com/gym/100231/attachments Description 给你n个区间,告诉你每个区间内都有ci个数 然后你需要 ...

  7. Codeforces 482B Interesting Array(线段树)

    题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,如今有M个限制,每一个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 ...

  8. codeforces 383C Propagating tree 线段树

    http://codeforces.com/problemset/problem/383/C 题目就是说,  给一棵树,将一个节点的值+val, 那么它的子节点都会-val, 子节点的子节点+val. ...

  9. CodeForces 228D. Zigzag(线段树暴力)

    D. Zigzag time limit per test 3 seconds memory limit per test 256 megabytes input standard input out ...

随机推荐

  1. wget命令8种实用用法

    大家好,我是良许. wget 是一个可以从网络上下载文件的免费实用程序,它的工作原理是从 Internet 上获取数据,并将其保存到本地文件中或显示在你的终端上. 这实际上也是大家所使用的浏览器所做的 ...

  2. jquery-无缝滚动

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. [火星补锅] 非确定性有穷状态决策自动机练习题Vol.3 T3 && luogu P4211 [LNOI2014]LCA 题解

    前言: 这题感觉还是很有意思.离线思路很奇妙.可能和二次离线有那么一点点相似?当然我不会二次离线我就不云了. 解析: 题目十分清真. 求一段连续区间内的所有点和某个给出的点的Lca的深度和. 首先可以 ...

  4. 2021.8.8考试总结[NOIP模拟33]

    T1 Hunter 考场上一看期望直接状压拿了$45pts$跑了.结果正解只用$4$行? 把问题转化为一号猎人之前死的猎人数的期望加一. 期望的线性性. 对每个猎人$i$,$w_i+w_1$种情况中有 ...

  5. Python AttributeError: module 'sys' has no attribute 'setdefaultencoding'

    Python 3 与 Python 2 有很大的区别,其中Python 3 系统默认使用的就是utf-8编码. 所以,对于使用的是Python 3 的情况,就不需要sys.setdefaultenco ...

  6. Linux内核 fork 源码分析

    内核版本:linux-4.4.18 源码位置:这里 fork相关的代码最终执行的函数为_do_fork(),下面按照顺序分析下_do_fork(): 首先判断是否需要trace(跟踪)这个进程,这一步 ...

  7. /etc/passwd 和 /etc/shadows 详解

    linux操作系统上的用户如果需要登录主机,当其输入用户名和密码之后: 首先在/etc/passwd文件中查找是否有你的账号,如果没有无法登录,如果有的话将该用户的UID和GID读出来,此外将此用户的 ...

  8. k8s入坑之路(13)服务迁移(定时任务 微服务 传统服务)

    定时任务迁移kubernetes 服务迁移步骤 1.安装好java 2.安装好maven 项目打包 mvn package 测试传参运行 java -cp cronjob-demo-1.0-SNAPS ...

  9. pip 常用命令小结

    pip 常用命令小结 pip这个工具我们经常会用到,毕竟python 是一门以第三方库庞大而著名的编程语言,所以我们总会用pip 安装一些依赖库,当然这只是pip 最常用的一个命令,下面就来介绍一下 ...

  10. PTA 7-7 六度空间 (30分)

    PTA 7-7 六度空间 (30分) "六度空间"理论又称作"六度分隔(Six Degrees of Separation)"理论.这个理论可以通俗地阐述为:& ...