@codeforces - 674G@ Choosing Ads
@description@
给定长度为 n 的序列,m 次询问以及参数 p。共有两类询问:
(1)"1 l r id":将区间 [l, r] 的颜色改成 id。
(2)"2 l r":对于区间 [l, r],输出不超过 100/p 种颜色,要求在区间内占比超过 >= p% 的颜色都应该被输出。
@solution@
看了好几遍题意才看懂那个奇怪的输出方法。。。
其实就是让你找到另一个判定条件,使得题目给的判定条件是你所找的判定条件的充分条件。。。
考虑 p >= 51 时,该问题是寻找区间占比 > 1/2 的数的充分条件。
这个问题。。。其实是一个经典问题(网上搜得到很多相关博客)。
寻找区间占比 > 1/2 的数(如果存在),只需要每次选中 2 个不同的数并同时消去,最后剩下的数就是所寻找的数。
类似地,可以大胆猜测寻找区间占比 > 1/k 的数(如果存在):只需要每次选中 k 个不同的数并同时消去,最后所剩下的 k-1 个数就是所寻找的数。
正确性?假如原先有 n 个数,其中 r 个为 x,且 r/n > 1/k。
假如你所选的 k 个数不包含 x,则新的占比 r/(n - k) > 1/k(这是显然的);否则,新的占比 (r - 1)/(n - k) > 1/k(这也是显然的)。
即操作一次后占比依然 > 1/k。那么归纳到 n < k 时,就可以得证了。
注意到只有前提 “存在这样的数”,两个过程才是等价的。
也就是说找出来的数不一定是区间占比 > 1/k 的数,但区间占比 > 1/k 的数一定会被找出。两者形成充分条件。
这也就用上了题目那奇怪的条件。
先找到最小的整数 k 使得 x > 1/k 的充分条件为 x >= p%。
考虑用数据结构(线段树)加速这一过程:我们对于每个结点只维护区间内删完后剩下的 <= k-1 个数以及它们的出现次数 cnt。
合并两个区间的信息时,只需要把一个区间的数往另一个区间插入。
分情况简单讨论一下,当数的种类数 = k 的时候就把这些数的出现次数 cnt 同时减去出现次数最少的数的出现次数 min{cnt}。
@accepted code@
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
#define mp make_pair
#define fi first
#define se second
const int MAXN = 150000;
int n, m, p, q;
struct node{
pii a[5]; int s;
node() {s = 0;}
node(pii x) {a[0] = x, s = 1;}
friend node insert(const node &A, const pii &x) {
node B = A;
for(int i=0;i<B.s;i++)
if( B.a[i].se == x.se ) {
B.a[i].fi += x.fi;
return B;
}
if( B.s < q )
B.a[B.s++] = x;
else {
int mn = 0;
for(int i=0;i<B.s;i++)
if( B.a[i].fi < B.a[mn].fi ) mn = i;
if( B.a[mn].fi >= x.fi ) {
for(int i=0;i<B.s;i++)
B.a[i].fi -= x.fi;
}
else {
pii y = B.a[mn]; B.a[mn] = x;
for(int i=0;i<B.s;i++)
B.a[i].fi -= y.fi;
}
}
return B;
}
friend node merge(const node &A, const node &B) {
if( A.s == 0 ) return B;
if( B.s == 0 ) return A;
node C = A;
for(int i=0;i<B.s;i++)
C = insert(C, B.a[i]);
return C;
}
};
int a[MAXN + 5];
struct segtree{
#define lch (x << 1)
#define rch (x << 1 | 1)
int le[4*MAXN + 5], ri[4*MAXN + 5], tg[4*MAXN + 5];
node nd[4*MAXN + 5];
void pushup(int x) {
nd[x] = merge(nd[lch], nd[rch]);
}
void pushdown(int x) {
if( tg[x] ) {
nd[lch] = node(mp(ri[lch]-le[lch]+1, tg[x]));
nd[rch] = node(mp(ri[rch]-le[rch]+1, tg[x]));
tg[lch] = tg[rch] = tg[x], tg[x] = 0;
}
}
void build(int x, int l, int r) {
le[x] = l, ri[x] = r, tg[x] = 0;
if( l == r ) {
nd[x] = node(mp(1, a[l]));
return ;
}
int m = (l + r) >> 1;
build(lch, l, m), build(rch, m + 1, r);
pushup(x);
}
void modify(int x, int l, int r, int k) {
if( l > ri[x] || r < le[x] )
return ;
if( l <= le[x] && ri[x] <= r ) {
nd[x] = node(mp(ri[x]-le[x]+1, k)), tg[x] = k;
return ;
}
pushdown(x);
modify(lch, l, r, k), modify(rch, l, r, k);
pushup(x);
}
node query(int x, int l, int r) {
if( l > ri[x] || r < le[x] )
return node();
if( l <= le[x] && ri[x] <= r )
return nd[x];
pushdown(x);
return merge(query(lch, l, r), query(rch, l, r));
}
}T;
void solve(int l, int r) {
node nd = T.query(1, l, r);
printf("%d", nd.s);
for(int i=0;i<nd.s;i++)
printf(" %d", nd.a[i].se);
puts("");
}
int main() {
scanf("%d%d%d", &n, &m, &p), q = 100 / p;
for(int i=1;i<=n;i++) scanf("%d", &a[i]);
T.build(1, 1, n);
for(int i=1;i<=m;i++) {
int op; scanf("%d", &op);
if( op == 1 ) {
int l, r, id; scanf("%d%d%d", &l, &r, &id);
T.modify(1, l, r, id);
}
else {
int l, r; scanf("%d%d", &l, &r);
solve(l, r);
}
}
}
@details@
没错我也开始做集训队作业了。
什么?集训队作业一共 150 道题?抱歉告辞告辞。
没见过经典模型的我一开始本来想的是随机抽样选点(大概率选中占比更多的颜色) + 判定,结果不是 WA 就是 TLE。
这样来来回回 15 次过后(途中还发现系统库的rand函数好像有些点无论选什么种子都找不到)我决定还是看看题解。
然后我就死了。。。
@codeforces - 674G@ Choosing Ads的更多相关文章
- Codeforces 643G - Choosing Ads(线段树)
Codeforces 题目传送门 & 洛谷题目传送门 首先考虑 \(p>50\) 的时候怎么处理,也就是求一个区间的绝对众数.我们知道众数这个东西是不能用线段树直接维护的,因为对于区间 ...
- 题解-CF643G Choosing Ads
CF643G Choosing Ads \(n\) 和 \(m\) 和 \(p\) 和序列 \(a_i(1\le i\le n)\).\(m\) 种如下操作: 1 l r id 令 \(i\in[l, ...
- 「CF643G」 Choosing Ads
「CF643G」 Choosing Ads 传送门 如果你知道摩尔投票法可以扩展事实上是个一眼题,又好写又好调. 首先摩尔投票法是用来求众数定义为超过所有数个数一半的数的一个算法. 大致算法流程: 将 ...
- Codeforces 219D. Choosing Capital for Treeland (树dp)
题目链接:http://codeforces.com/contest/219/problem/D 树dp //#pragma comment(linker, "/STACK:10240000 ...
- Codeforces 219D Choosing Capital for Treeland
http://codeforces.com/problemset/problem/219/D 题目大意: 给出一棵树,但是它的边是有向边,选择一个城市,问最少调整多少条边的方向能使一个选中城市可以到达 ...
- (纪念第一道完全自己想的树DP)CodeForces 219D Choosing Capital for Treeland
Choosing Capital for Treeland time limit per test 3 seconds memory limit per test 256 megabytes inpu ...
- CodeForces 219D Choosing Capit
题目链接:http://codeforces.com/contest/219/problem/D 题目大意: 给定一个n个节点的数和连接n个节点的n - 1条有向边,现在要选定一个节点作为起始节点,从 ...
- Codeforces 219D - Choosing Capital for Treeland(树形dp)
http://codeforces.com/problemset/problem/219/D 题意 给一颗树但边是单向边,求至少旋转多少条单向边的方向,可以使得树上有一点可以到达树上任意一点,若有多个 ...
- Codeforces 219D Choosing Capital for Treeland:Tree dp
题目链接:http://codeforces.com/problemset/problem/219/D 题意: 给你一棵树,n个节点. 树上的边都是有向边,并且不一定是从父亲指向儿子的. 你可以任意翻 ...
随机推荐
- zz 关于插入意向间隔锁( insert intention gap lock)产生的死锁问题
出处: http://www.cnblogs.com/sunss/p/3166550.html 昨天看到一个很有意思的死锁,拿来记录下: 环境:deadlock on 事务隔离级别: read com ...
- CF808E Selling Souvenirs
题目链接: http://codeforces.com/contest/808/problem/E 题目大意: Petya 有 n 个纪念品,他能带的最大的重量为 m,各个纪念品的重量为 wi,花费为 ...
- Java术语
- 循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作
之前一直采用VS进行各种前端后端的开发,随着项目的需要,正逐步融合纯前端的开发模式,开始主要选型为Vue + Element 进行BS前端的开发,后续会进一步整合Vue + AntDesign的界面套 ...
- layui 数据表格最简单的点击事件
//定义点击事件 table.on('row(test)', function(obj){ console.log(obj.tr) //得到当前行元素对象 console.log(obj.data) ...
- pip命令报错“no perl script found in input”
windows10,命令行下使用pip命令时报错,python运行成功,且环境变量已经设置,可能是更新了新版本pip所导致 解决方案: 一.使用pip.exe+命令可解决这个问题 二.进行python ...
- pytest常用命令参数
pytest 参数 1.参数:-s 运行过程中执行print打印函数:pytest -s,以下两个输出 上边带参数,下边不带 2.参数: --collect-only 收集将要执行的用例,但不会执行用 ...
- 这次终于可以愉快的进行 appium 自动化测试了
appium 是进行 app 自动化测试非常成熟的一套框架.但是因为 appium 设计到的安装内容比较多,很多同学入门都跪在了环境安装的部分.本篇讲述 appium 安卓环境的搭建,希望让更多童鞋轻 ...
- Longest Mountain in Array 数组中的最长山脉
我们把数组 A 中符合下列属性的任意连续子数组 B 称为 “山脉”: B.length >= 3 存在 0 < i < B.length - 1 使得 B[0] < B[1] ...
- Matlab矩阵学习三 矩阵的运算
Matlab矩阵的运算 一.矩阵的加减 在matlab中,矩阵的加减和数的加减符号一样,都是"+"和”-“,不同的是两个进行运算的矩阵维度必须相同 二.数乘 三.乘法 矩阵乘法 ...