题解-CF643G Choosing Ads
\(n\) 和 \(m\) 和 \(p\) 和序列 \(a_i(1\le i\le n)\)。\(m\) 种如下操作:
1 l r id令 \(i\in[l,r]:a_i=id\)。2 l r输出至多 \(\lfloor\frac{100}{p}\rfloor\) 个数,其中包括所有 \([l,r]\) 区间内出现 \(\ge\lceil\frac{p(r-l+1)}{100}\rceil\) 次的 \(a_i\)。
数据范围:\(1\le n,m,id,a_i\le 150000\),\(20\le p\le 100\),\(1\le l\le r\le n\)
一眼想法:搞个线段树,节点存该区间出现频率 \(\ge\frac{p}{100}\) 的 \(a_i\) 值和出现次数存下来,合并。
这个做法的依据: 如果区间 \([l,r]\) 中 \(a_i\) 出现频率 \(\ge\frac{p}{100}\),\([l,mid]\) 或 \([mid+1,r]\) 必中有一个也满足。
这个做法的 \(\tt bug\): 若 \(a_i\) 存在于 \([l,mid]\) 的节点中而不存在于 \([mid+1,r]\) 的节点中(或反过来),节点的信息难以合并。
于是,我这个思维愚钝的大蒟蒻就没在考场上做出来。
这题的正解类似以前的一道经典题:一个序列求众数(出现频率 \(\ge \frac 12\)),空间复杂度 只能 \(\Theta(1)\)。
做法是记录 \(now\) 和 \(cnt\)。新加入数 \(a\) 的时候,如果 \([now=a]\),\(cnt++\);如果 \([now\not=a]\),\(cnt--\),如果 \([cnt<0]\),\(cnt=1\) 并且 \(now=a\)。
这题也类似。出现频率 \(\ge\frac{p}{100}\) 的数至多 \(\lfloor\frac{100}{p}\rfloor\) 个,所以可以记录 \(\lfloor\frac{100}{p}\rfloor\) 个 \(now_i\) 和 \(cnt_i\)。
每次加入 \(a\),如果 \([now_i=a]\),\(cnt_i++\);如果 \([now_i\not=a]\),\(cnt_i--\),如果 \([cnt_i<0]\),\(cnt_i=1\) 并且 \(now_i=a\)。
这样的话虽然只存了 \(\lfloor\frac{100}{p}\rfloor\) 种数,但已经完全反映了区间内数的数量对比,所以节点信息可以直接合并。
最后每个节点或许会存下不满足出现频率 \(\ge\frac{p}{100}\) 的值,但是输出只要包含答案就行了(题中说的)。
- 代码
稍微长了点,但是没有坑人的细节,直接写就好了。
#include <bits/stdc++.h>
using namespace std;
//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define b(a) a.begin()
#define e(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
//Data
const int N=150000;
int n,m,p,pl,a[N+7];
//Segmenttree
typedef vector<pair<int,int>> vpii;
#define mid ((l+r)>>1)
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
int mk[N<<2]; vpii ca[N<<2]; //pair.x 表示 now_i,pair.y 表示 cnt_i
void fill(vpii&v,int id,int c){v.clear(),v.pb(mp(id,c));}
vpii operator+(vpii p,vpii q){ //将 p 中的数一一加入 q
vpii r;
for(auto&u:p){
int ok=0; for(auto&v:q)if(u.x==v.x){v.y+=u.y,ok=1;break;}
if(ok) continue; q.pb(u); if(sz(q)<=pl) continue;
int mn=n; for(auto&v:q) mn=min(mn,v.y);
r.clear(); for(auto&v:q)if(v.y-mn) r.pb(mp(v.x,v.y-mn)); q=r;
}
return q;
}
void down(int k,int l,int r){
if(!mk[k]) return;
fill(ca[k<<1],mk[k],mid-l+1),fill(ca[k<<1|1],mk[k],r-mid);
mk[k<<1]=mk[k<<1|1]=mk[k],mk[k]=0;
}
void build(int k=1,int l=1,int r=n){
if(l==r) return void(fill(ca[k],a[l],1));
build(ls),build(rs),ca[k]=ca[k<<1]+ca[k<<1|1];
}
void fix(int x,int y,int z,int k=1,int l=1,int r=n){
if(x<=l&&r<=y) return void((fill(ca[k],z,r-l+1),mk[k]=z)); down(k,l,r);
if(mid>=x) fix(x,y,z,ls); if(mid<y) fix(x,y,z,rs);
ca[k]=ca[k<<1]+ca[k<<1|1];
}
vpii query(int x,int y,int k=1,int l=1,int r=n){
if(x<=l&&r<=y) return ca[k]; down(k,l,r);
vpii res; if(mid>=x) res=res+query(x,y,ls); if(mid<y) res=res+query(x,y,rs);
return res;
}
void Print(int k=1,int l=1,int r=n){ //调试用的,我这个蒟蒻,总是代码写挂
printf("[%d,%d,%d]:mk=%d\n",k,l,r,mk[k]);
for(auto&u:ca[k]) printf("(%d,%d)",u.x,u.y);puts("");
if(l==r) return; down(k,l,r);
Print(ls),Print(rs);
}
//Main
int main(){
scanf("%d%d%d",&n,&m,&p),pl=100/p;
for(int i=1;i<=n;i++) scanf("%d",&a[i]); build();
for(int i=1;i<=m;i++){
int o; scanf("%d",&o);
if(o==1){int l,r,id; scanf("%d%d%d",&l,&r,&id); fix(l,r,id);}
else {
int l,r; scanf("%d%d",&l,&r);
vpii res=query(l,r); printf("%d",sz(res));
for(auto&u:res) printf(" %d",u.x); puts("");
}
// puts("+++++");
// Print();
// puts("-----");
}
return 0;
}
祝大家学习愉快!
题解-CF643G Choosing Ads的更多相关文章
- [cf643G]Choosing Ads
首先对于$p>50$,有经典的做法,即不断删去区间中不同的两数,最终剩下的即为出现次数超过一半的数(或没有),用线段树维护即可 那么对于$p\le 50$,类似的,即删去区间中不同的$\lflo ...
- 「CF643G」 Choosing Ads
「CF643G」 Choosing Ads 传送门 如果你知道摩尔投票法可以扩展事实上是个一眼题,又好写又好调. 首先摩尔投票法是用来求众数定义为超过所有数个数一半的数的一个算法. 大致算法流程: 将 ...
- @codeforces - 674G@ Choosing Ads
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定长度为 n 的序列,m 次询问以及参数 p.共有两类询问: ...
- Codeforces 643G - Choosing Ads(线段树)
Codeforces 题目传送门 & 洛谷题目传送门 首先考虑 \(p>50\) 的时候怎么处理,也就是求一个区间的绝对众数.我们知道众数这个东西是不能用线段树直接维护的,因为对于区间 ...
- 多校联训 DS 专题
CF1039D You Are Given a Tree 容易发现,当 \(k\) 不断增大时,答案不断减小,且 \(k\) 的答案不超过 \(\lfloor\frac {n}{k}\rfloor\) ...
- Codeforces Choosing Laptop 题解
这题实在是太水了,具体看注释 蒟蒻的方法是一边找过时的电脑一边比大小 蒟蒻不才,只会C++ 其实还会free basic,但它已经过时了 附: 本题洛谷网址 Codeforces网址 希望蒟蒻的题解能 ...
- 【题解】codeforces 219D Choosing Capital for Treeland 树型dp
题目描述 Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市.每条道路只能单向通行.现在政府需要决定选择哪个城市为首都.假如城市i成为了首都,那么为了使首都能到达任意一 ...
- 【codeforce 219D】 Choosing Capital for Treeland (树形DP)
Choosing Capital for Treeland Description The country Treeland consists of n cities, some pairs of t ...
- usaco training 4.2.4 Cowcycles 题解
Cowcycles题解 Originally by Don Gillies [International readers should note that some words are puns on ...
随机推荐
- @AliasFor注解
@AliasFor注解 @AliasFor是一个注解,用于为注解属性声明别名. 代码如下:它有两个属性value和attribute @AliasFor注解注释了 自身,并且value和attribu ...
- linux之DNS服务
1.DNS (Domain Name Service 域名解析) DNS是因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网而不需要记忆能够直接被机器识别的IP. BI ...
- 验证rbd的缓存是否开启
简单快速的在客户端验证rbd的cache是否开启 首先修改配置文件 在ceph.conf中添加: [client] rbd cache = true rbd cache writethrough un ...
- Linux(centos6.8)配置Mysql环境
1.下载mysql安装包 https://downloads.mysql.com/archives/community/ 2.查询mysql信息 [1]查询mysql是否已经安装 [root@plut ...
- rocketmq详解-[个人版]-第一章
一.消息队列概述 1.1.消息队列由来 在运维场景中,我们经常会存在如下场景:一旦出现S1异常,C1将因为S1的影响而异常(C为客户端,s为服务端) 当然可以通过添加多个S的方式,实现高可用.但这样会 ...
- 算法基础——KMP字符串匹配
原题链接 题目: 给定一个模式串S,以及一个模板串P,所有字符串中只包含大小写英文字母以及阿拉伯数字. 模板串P在模式串S中多次作为子串出现. 求出模板串P在模式串S中所有出现的位置的起始下标. 输入 ...
- 企业级工作流解决方案(七)--微服务Tcp消息传输模型之消息编解码
Tcp消息传输主要参照surging来做的,做了部分裁剪和改动,详细参见:https://github.com/dotnetcore/surging Json-rpc没有定义消息如何传输,因此,Jso ...
- JUC详解--【Foam番茄】
1.什么是JUC java.util 工具包 业务:普通的线程代码 Thread Runnable 没有返回值,效率相比于 Callable 相对较低! 2.线程和进程 进程:一个程序,QQ.exe ...
- 【震惊】手把手教你用python做绘图工具(一)
在这篇博客里将为你介绍如何通过numpy和cv2进行结和去创建画布,包括空白画布.白色画布和彩色画布.创建画布是制作绘图工具的前提,有了画布我们就可以在画布上尽情的挥洒自己的艺术细胞. 还在为如何去绘 ...
- Prafab Varient 预制体变体
预制体与类的类比思维: 预制体相当于一个类,当它应用到场景当中,就是一个实例. 类的继承特性也充分运用到预制体中,即预制体变体. 相似预制体的需求场景: 例子1:多个游戏的窗口 ...