「Luogu P5368 [PKUSC2018]真实排名」
PKUSC签到题
题目大意
给出一个长度为 \(N\) 的序列,序列中有 \(K\) 个数会乘二,对于每个数计算在乘二后大于等于这个数的个数与乘二前没有发生变化的方案数.
分析
思路很清晰,可以将答案分为两个部分计算
当前位置的数没有乘二时
当前位置没有乘二,所以所有大于等于自己的元素是否乘二每有影响,如果一个数小于这个数的一半(不可以等于)那么这个数如果乘二也不会产生影响.于是可以计算出大于等于这个数的个数 \(+\) 小于这个数一半的数的个数.接着只需要通过组合数就可以计算你出来了.
当前位置的数乘二时
用一张图来解释起来比较简单:(下图序列为 \(a=[1,3,3,4,6,7]\))
假如开始时的排完序后的序列是上面这个样子,对于第二个数大于等于它的数的个数为 \(5\) (包括自己).现在需要将它的高度翻倍:
可以发现大于等于它的数的个数只剩下了一个,为了保证大于等于这个数的个数不变,在当前数乘二后必须将大于等于这个数小于这个数乘二的数都乘上二.
虽然最后的序列可能不再是有序的,但是对于这个数大于等于它的数的个数没有改变,可以发现原来小于它的数这时如果乘二并不会影响,大于这个数两倍的数乘二自然也不会产生影响啦,于是又可以通到组合数直接计算了.
组合数部分(会的可以跳过)
看到绝大多数的题解都不会涉及这部分内容,但是为了保证大多数人可以看懂,还是来写一下.
在 \(N\) 个数中选 \(M\) 个数的方案数: \(C_{M}^{N} = \frac{N!}{M!(N-M)!}\) 但是在本题中 \(N,M\) 都是 \(1 \times 10^5\) 级别自然不可以暴力计算,可以发现在这个公式由三个阶乘组成,于是自然会想到预处理阶乘,然后计算逆元.这样就可以做到 \(O(N\log_2N)\) 预处理阶乘和逆元,\(O(1)\) 计算,但是\(O(N\log_2N)\)还是有点慢了(虽然在本题中应该可以过),在一些 \(2 \times 10^6 \leq N\) (差不多)时这个时间复杂度就很容易TLE了,\(a\) 的逆元可以理解为 \(\frac{1}{a}\) 所以说 \(\frac{1}{i!}=\frac{1}{(i+1)!} \times i\),这样就可以先处理出 \(N!\) 的逆元,接着只需要 \(O(N)\) 的时间复杂度就可以计算出逆元了.
代码实现
#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int maxN=3e5+7;
const int mod=998244353;
int Mod(long long a)//写一个Mod函数
{
a%=mod;
a+=mod;
a%=mod;
return a;
}
long long Inv(long long a,int b=mod-2)//计算一个数的逆元,其实就是一个快速幂
{
long long result=1;
while(b)
{
if(b&1)
{
result*=a;
result%=mod;
}
a*=a;
a%=mod;
b/=2;
}
return result;
}
long long fac[maxN];//计算阶乘
long long inv[maxN];//计算逆元
int N,M;
int arr[maxN*2];
int sor[maxN*3];//用来离散化
int sum[maxN*3];
int tot=0;
int k;
map<int,int>Hash;//用来离散化
//因为比较懒,于是就先了一颗权值线段树来维护
struct SegmentTree
{
int sum;
}sgt[maxN*4];
//线段树标准define
#define LSON (now<<1)
#define RSON (now<<1|1)
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
void PushUp(int now)//合并左右子树的值
{
sgt[now].sum=sgt[LSON].sum+sgt[RSON].sum;
}
void Build(int now=1,int left=1,int right=tot)//建树
{
if(left==right)//叶节点直接赋值
{
sgt[now].sum=sum[left];
return;
}
Build(LEFT);
Build(RIGHT);
PushUp(now);//合并
}
//本题不需要修改
int QueryBigger(int num,int now=1,int left=1,int right=tot)
//计算大于等于的数的个数
{
if(num<=left)
{
return sgt[now].sum;
}
if(right<num)
{
return 0;
}
return QueryBigger(num,LEFT)+QueryBigger(num,RIGHT);
}
int QuerySmaller(int num,int now=1,int left=1,int right=tot)
//计算小于等于的数的个数
{
if(right<=num)
{
return sgt[now].sum;
}
if(num<left)
{
return 0;
}
return QuerySmaller(num,LEFT)+QuerySmaller(num,RIGHT);
}
int QuerySmaller_(int num,int now=1,int left=1,int right=tot)
//计算小于的数的个数
{
if(right<num)
{
return sgt[now].sum;
}
if(num<=left)
{
return 0;
}
return QuerySmaller_(num,LEFT)+QuerySmaller_(num,RIGHT);
}
//用完就清空define,防止与其他地方冲突
#undef LSON
#undef RSON
#undef MIDDLE
#undef LEFT
#undef RIGHT
int main()
{
scanf("%d%d",&N,&k);
int cnt=0;
fac[1]=1;
REP(i,2,N)//预处理阶乘
{
fac[i]=Mod(fac[i-1]*i);
}
inv[N]=Inv(fac[N]);//计算N!的逆元
DOW(i,N-1,0)
{
inv[i]=Mod(inv[i+1]*(i+1));//通过优化以后的方法O(N)计算所有阶乘的逆元
}
REP(i,1,N)
{
scanf("%d",&arr[i]);//对于每个数它与它的两倍和一半会在操作中用到
sor[++cnt]=arr[i];
sor[++cnt]=arr[i]/2;
sor[++cnt]=arr[i]*2;
}
//离散化部分
sort(sor+1,sor+1+cnt);
sor[0]=-114514233;
REP(i,1,cnt)
{
if(sor[i]!=sor[i-1])
{
Hash[sor[i]]=++tot;
}
}
REP(i,1,N)//将数放入
{
sum[Hash[arr[i]]]++;
}
Build();//建树
long long answer;//计算答案
int all;//当前数不乘二时有多少数乘二不会造成影响
int must;//计算如果当前数乘二时有多少数必须乘二
int p,q;//计算时需要用到的一些变量
REP(i,1,N)
{
all=QueryBigger(Hash[arr[i]]);//大于等于自身的数的个数肯定不会影响
if(arr[i]&1)all+=QuerySmaller(Hash[arr[i]/2]);//这里需要根据这个数奇偶性查询乘二后仍然小于这个数的个数
else all+=QuerySmaller_(Hash[arr[i]/2]);
all-=1;//将自己减去
answer=Mod(Mod(fac[all]*inv[k])*inv[all-k]);//通过组合数公式计算方案数
if(arr[i]==0)must=1;else//需要特判一下0
must=QuerySmaller_(Hash[arr[i]*2])-QuerySmaller_(Hash[arr[i]]);//必须乘二的数的个数为小于这个数乘二的数的个数-小于这个数的数的个数
if(must<=k)//如果可以做到全部乘二
{
p=N-must;//乘二不会造成影响的数的个数
q=k-must;//还可以有几个数乘二
answer+=Mod(Mod(fac[p]*inv[q])*inv[p-q]);//计算方案数
}
printf("%lld\n",answer%mod);//输出答案
}
return 0;
}
「Luogu P5368 [PKUSC2018]真实排名」的更多相关文章
- Luogu P5368 [PKUSC2018]真实排名
老年选手只会做SB题了(还调了好久) 很容易想到分类讨论,按第\(i\)个人有没有翻倍来算 若\(a_i\)未翻倍,显然此时将\([0,\lceil \frac{a_i}{2}\rceil)\)的数和 ...
- 【LOJ4632】[PKUSC2018]真实排名
[LOJ4632][PKUSC2018]真实排名 题面 终于有题面啦!!! 题目描述 小 C 是某知名比赛的组织者,该比赛一共有 \(n\) 名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排 ...
- [PKUSC2018]真实排名
[PKUSC2018]真实排名 题目大意: 有\(n(n\le10^5)\)个人,每个人有一个成绩\(A_i(0\le A_i\le10^9)\).定义一个人的排名为\(n\)个人中成绩不小于他的总人 ...
- BZOJ_5368_[Pkusc2018]真实排名_组合数
BZOJ_5368_[Pkusc2018]真实排名_组合数 Description 小C是某知名比赛的组织者,该比赛一共有n名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排名是:成绩不小于他 ...
- [PKUSC2018]真实排名——线段树+组合数
题目链接: [PKUSC2018]真实排名 对于每个数$val$分两种情况讨论: 1.当$val$不翻倍时,那么可以翻倍的是权值比$\frac{val-1}{2}$小的和大于等于$val$的. 2.当 ...
- bzoj5368 [Pkusc2018]真实排名
题目描述: bz luogu 题解: 组合数计数问题. 首先注意排名指的是成绩不小于他的选手的数量(包括他自己). 考虑怎么增大才能改变排名. 小学生都知道,对于成绩为$x$的人,让他自己不动并让$\ ...
- BZOJ5368:[PKUSC2018]真实排名(组合数学)
Description 小C是某知名比赛的组织者,该比赛一共有n名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排名是:成绩不小于他的选手的数量(包括他自己). 例如如果333位选手的成绩分别 ...
- bzoj 5368: [Pkusc2018]真实排名
Description 小C是某知名比赛的组织者,该比赛一共有n名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排名是 :成绩不小于他的选手的数量(包括他自己).例如如果3位选手的成绩分别是[ ...
- 【洛谷5368】[PKUSC2018] 真实排名(组合数学)
点此看题面 大致题意: 有\(n\)个数字,定义一个数的排名为不小于它的数的个数.现要随机将其中\(k\)个数乘\(2\),求对于每个数有多少种方案使其排名不变. 分类讨论 对于这种题目,我们可以分类 ...
随机推荐
- git 提交的时候 建立排除文件夹或者文件
1.在Git的根仓库下 touch .gitignore 2.编辑这个文件
- jquery grid 获取选中的行的数据,以及获取所有行的方法
https://blog.csdn.net/shenqingkeji/article/details/52861319
- np.c_与np.r_
import sys reload(sys) sys.setdefaultencoding('utf-8') import numpy as np def test(): ''' numpy函数np. ...
- Multisim 之逻辑转换仪
1.逻辑转换仪的作用 已知逻辑电路图 1)根据逻辑电路图转换成逻辑表达式 2)化简逻辑表达式 3)列出真值表 已知真值表 1)根据真值表转化成逻辑表达式 2)根据逻辑表达式生成逻辑电路 2.如何在Mu ...
- opencv:轮廓匹配
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...
- Mysql sql语句技巧与优化
一.常见sql技巧 1.正则表达式的使用 2.巧用RAND()提取随机行 mysql数据库中有一个随机函数rand()是获取一个0-1之间的数,利用这个函数和order by一起能够吧数据随机排序, ...
- html()和append()
html()方法会替换原有内容,append() 方法在被选元素的结尾插入指定内容.prepend() 方法在被选元素的开头插入指定内容 appendChild() 方法向节点添加最后一个子节点.pr ...
- 吴裕雄 python 机器学习——数据预处理过滤式特征选取VarianceThreshold模型
from sklearn.feature_selection import VarianceThreshold #数据预处理过滤式特征选取VarianceThreshold模型 def test_Va ...
- iptables详解(1):命令参数解析
1.编辑配置网卡信息方法: 1.1 编辑配置文件:vim /etc/sysconfig/network-scripts/ifcfg-lo 1.2 nmtui命令:图形化配置 1.3 nm-connec ...
- 117. 填充每个节点的下一个右侧节点指针 II
Q: 给定一个二叉树 struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每个 next 指针,让这个指针指向其下一个右 ...