Loj#6432「PKUSC2018」真实排名(二分查找+组合数)
题面
题解
普通的暴力是直接枚举改或者不改,最后在判断最后对哪些点有贡献。
而这种方法是很难优化的。所以考虑在排序之后线性处理。首先先假设没有重复的元素
struct Node { int poi, id; } a[N];
bool operator < (const Node &a, const Node &b) { return a.poi < b.poi; }
bool operator < (const Node &a, const int &b) { return a.poi < b; }
bool operator < (const int &a, const Node &b) { return a < b.poi; }
int main() {
read(n);
for(int i = 1; i <= n; ++i) read(a[i].poi), a[i].id = i;
sort(a + 1, a + n + 1);
}
对于一个点,我们同样是枚举它改或者不改,但是,接着我们来判断哪些点的变化可以对这个点产生贡献,
决策1:不改
不改的话,那么这个元素后面的元素不管变还是不变都可以产生贡献,假设当前处理到$i$,则其后面有$n-i$个元素。
接着考虑前面的元素,前面的元素改变可以对它产生贡献当且仅当它小于$a_i/2$。这里可以使用二分查找。假设一共有$site$个元素满足上面这个条件。
则这个决策所产生的贡献为:$C_{n-i+site}^k$
决策2:改
当这个元素改的时候,怎么保证它的$rank$不变呢?那么就要保证区间$[a_i,2a_i]$这个区间内的所有数字都要变。同样可以二分来确定这个区间内有多少个元素。假设右界为$tmp$,则有$tot=tmp-i+1$个元素是必须要变的
则这个决策所产生的贡献为:$C_{n-tot}^{k-tot}$
重复的元素
之前的所有决策都是在元素不重复的情况下计算的贡献。那么当元素重复时,怎么计算呢?假设现在同一个元素已经出现了$cf$次。
考虑不改的决策,由于$rank$的含义是大于等于它的数不变,所以这个决策的贡献变为:$C_{n-i+site+cf-1}^k$
接着考虑改变的决策,同样,根据$rank$的定义,这些重复的数字也需要改变。所以$tot$变为:
$$
tot=tmp-i+1+cf-1=tmp-i+cf
$$
接着还有一些细节,比如对于$0$的特判(直接就是$C(n,k)$)之类的
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::lower_bound;
using std::upper_bound;
using std::min; using std::max;
using std::swap; using std::sort;
typedef long long ll;
template<typename T>
void read(T &x) {
int flag = 1; x = 0; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') flag = -flag; ch = getchar(); }
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); x *= flag;
}
const int N = 1e5 + 10, P = 998244353;
int n, k, ret[N], fac[N], inv[N];
struct Node { int poi, id; } a[N];
bool operator < (const Node &a, const Node &b) { return a.poi < b.poi; }
bool operator < (const Node &a, const int &b) { return a.poi < b; }
bool operator < (const int &a, const Node &b) { return a < b.poi; }
inline void add(int &x) { ++x; if(x == P) x = 0; }
int find(double val, int site) {
int l = 1, r = site - 1, ret = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(1. * a[mid].poi < val) ret = mid, l = mid + 1;
else r = mid - 1;
} return ret;
}
int C(int n, int m) {
if(n < m) return 0;
return (int)(1ll * (1ll * fac[n] * inv[m] % P) * inv[n - m] % P);
}
int qpow(int a, int b) {
int ret = 1;
for(; b; b >>= 1, a = 1ll * a * a % P) if(b & 1) ret = 1ll * ret * a % P;
return ret;
}
int main () {
read(n), read(k), fac[0] = inv[0] = 1;
for(int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % P;
inv[n] = qpow(fac[n], P - 2);
for(int i = n; i >= 1; --i) inv[i - 1] = 1ll * inv[i] * i % P;
for(int i = 1; i <= n; ++i) read(a[i].poi), a[i].id = i;
sort(a + 1, a + n + 1);
for(int i = 1, cf = 0; i <= n; ++i) {
if(a[i].poi == 0) { ret[a[i].id] = C(n, k); continue; }
int site = find(1. * a[i].poi / 2., i);
if(a[i].poi == a[i - 1].poi) ++cf;
else cf = 1;
(ret[a[i].id] += C(site + n - i + cf - 1, k)) %= P;//改的决策
int tmp = lower_bound(a + i + 1, a + n + 1, a[i].poi * 2) - a - 1;
if(tmp != -1) {
int tot = tmp - i + cf;
if(k >= tot) (ret[a[i].id] += C(n - tot, k - tot)) %= P;
}//不改的决策
}
for(int i = 1; i <= n; ++i) printf("%d\n", ret[i]);
return 0;
}
Loj#6432「PKUSC2018」真实排名(二分查找+组合数)的更多相关文章
- LOJ #6432. 「PKUSC2018」真实排名(组合数)
题面 LOJ #6432. 「PKUSC2018」真实排名 注意排名的定义 , 分数不小于他的选手数量 !!! 题解 有点坑的细节题 ... 思路很简单 , 把每个数分两种情况讨论一下了 . 假设它为 ...
- Loj 6432. 「PKUSC2018」真实排名 (组合数)
题面 Loj 题解 枚举每一个点 分两种情况 翻倍or不翻倍 \(1.\)如果这个点\(i\)翻倍, 要保持排名不变,哪些必须翻倍,哪些可以翻倍? 必须翻倍: \(a[i] \leq a[x] < ...
- LOJ 6432 「PKUSC2018」真实排名——水题
题目:https://loj.ac/problem/6432 如果不选自己,设自己的值是 x ,需要让 “ a<x && 2*a>=x ” 的非 x 的值不被选:如果选自己 ...
- LOJ #6432. 「PKUSC2018」真实排名
题目在这里...... 对于这道题,现场我写炸了......谁跟我说组合数O(n)的求是最快的?(~!@#¥¥%……& #include <cstdio> #include < ...
- 【LOJ】#6432. 「PKUSC2018」真实排名
题解 简单分析一下,如果这个选手成绩是0,直接输出\(\binom{n}{k}\) 如果这个选手的成绩没有被翻倍,那么找到大于等于它的数(除了它自己)有a个,翻倍后不大于它的数有b个,那么就从这\(a ...
- #6432. 「PKUSC2018」真实排名(组合数学)
题面 传送门 题解 这数据范围--这输出大小--这模数--太有迷惑性了-- 首先对于\(0\)来说,不管怎么选它们的排名都不会变,这个先特判掉 对于一个\(a_i\)来说,如果它不选,那么所有大于等于 ...
- 「PKUSC2018」真实排名(排列组合,数学)
前言 为什么随机跳题会跳到这种题目啊? Solution 我们发现可以把这个东西分情况讨论: 1.这个点没有加倍 这一段相同的可以看成一个点,然后后面的都可以. 这一段看成一个点,然后前面的不能对他造 ...
- 「PKUSC2018」真实排名(组合)
一道不错的组合数问题! 分两类讨论: 1.\(a_i\) 没有翻倍,那些 \(\geq a_i\) 和 \(a_j\times 2<a_i\) 的数就没有影响了.设 \(kth\) 为 \(a_ ...
- 「PKUSC2018」真实排名
题面 题解 因为操作为将一些数字翻倍, 所以对于一个数\(x\), 能影响它的排名的的只有满足\(2y\geq x\)或\(2x>y\)的\(y\) 将选手的成绩排序,然后考虑当前点的方案 1. ...
随机推荐
- java File (文档流)
一. 数据流的基本概念 1.数据流 在Java中把不同的数据源与程序之间的数据传输都抽象表述为“流”(stream),以实现相对统一和简单的输入/输出操作方式.传输中的数据就像流水一样,也称为数据 ...
- [BZOJ1076][SCOI2008]奖励关解题报告|状压DP
你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再吃). 宝 ...
- HDU 1175 连连看 (深搜+剪枝)
题目链接 Problem Description "连连看"相信很多人都玩过.没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子.如果某两个相同的棋子,可以 ...
- react-native中使用Echarts,自己使用WebView封装Echarts经验
1.工作中遇到的问题 我们在使用react-native肯定遇到过各种奇葩的问题,比如引入Echarts时候莫名报错,但是Echarts官网明显告诉我们可以懒加载的,这是因为基本上js大部分原生的组件 ...
- Microsoft Visual Studio TFS 切换登录用户的方法
方法一 用VS2012开发项目,一直困扰着我的是不知道怎么去切换TFS的登录帐户,因为每次TFS都是用默认的账户连接到TFS服务器,有时想切换另一个用户登录TFS服务器, 不知道从何下手,今天总算找到 ...
- chrome最小字体12px如何修改
在html标记样式里加入 <style> html { -webkit-text-size-adjust:none } </style> 这样的方式可以设置chrome字体小于 ...
- bzoj 2669 状压DP
因为最多有8个'X',所以我们可以用w[i][s]来表示现在我们填了前i个数,填的X的为S,因为每次新加进来的数都不影响前面的最小值,所以我们可以随便添加,这样就有了剩下所有位置的方案,每次都这样转移 ...
- 第一章:获取服务器服务banner
#!c:\\perl\\bin\\perl.exe #读取服务器的首行(banner) use IO::Socket; my $service = '121.201.67.177:ssh'; my $ ...
- Python3 使用 urllib 编写爬虫
什么是爬虫 爬虫,也叫蜘蛛(Spider),如果把互联网比喻成一个蜘蛛网,Spider就是一只在网上爬来爬去的蜘蛛.网络爬虫就是根据网页的地址来寻找网页的,也就是URL.举一个简单的例子,我们在浏览器 ...
- libSVM笔记之(一)在matlab环境下安装配置libSVM
本文为原创作品,转载请注明出处 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing 台湾林智仁教 ...