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]真实排名」的更多相关文章

  1. Luogu P5368 [PKUSC2018]真实排名

    老年选手只会做SB题了(还调了好久) 很容易想到分类讨论,按第\(i\)个人有没有翻倍来算 若\(a_i\)未翻倍,显然此时将\([0,\lceil \frac{a_i}{2}\rceil)\)的数和 ...

  2. 【LOJ4632】[PKUSC2018]真实排名

    [LOJ4632][PKUSC2018]真实排名 题面 终于有题面啦!!! 题目描述 小 C 是某知名比赛的组织者,该比赛一共有 \(n\) 名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排 ...

  3. [PKUSC2018]真实排名

    [PKUSC2018]真实排名 题目大意: 有\(n(n\le10^5)\)个人,每个人有一个成绩\(A_i(0\le A_i\le10^9)\).定义一个人的排名为\(n\)个人中成绩不小于他的总人 ...

  4. BZOJ_5368_[Pkusc2018]真实排名_组合数

    BZOJ_5368_[Pkusc2018]真实排名_组合数 Description 小C是某知名比赛的组织者,该比赛一共有n名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排名是:成绩不小于他 ...

  5. [PKUSC2018]真实排名——线段树+组合数

    题目链接: [PKUSC2018]真实排名 对于每个数$val$分两种情况讨论: 1.当$val$不翻倍时,那么可以翻倍的是权值比$\frac{val-1}{2}$小的和大于等于$val$的. 2.当 ...

  6. bzoj5368 [Pkusc2018]真实排名

    题目描述: bz luogu 题解: 组合数计数问题. 首先注意排名指的是成绩不小于他的选手的数量(包括他自己). 考虑怎么增大才能改变排名. 小学生都知道,对于成绩为$x$的人,让他自己不动并让$\ ...

  7. BZOJ5368:[PKUSC2018]真实排名(组合数学)

    Description 小C是某知名比赛的组织者,该比赛一共有n名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排名是:成绩不小于他的选手的数量(包括他自己). 例如如果333位选手的成绩分别 ...

  8. bzoj 5368: [Pkusc2018]真实排名

    Description 小C是某知名比赛的组织者,该比赛一共有n名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排名是 :成绩不小于他的选手的数量(包括他自己).例如如果3位选手的成绩分别是[ ...

  9. 【洛谷5368】[PKUSC2018] 真实排名(组合数学)

    点此看题面 大致题意: 有\(n\)个数字,定义一个数的排名为不小于它的数的个数.现要随机将其中\(k\)个数乘\(2\),求对于每个数有多少种方案使其排名不变. 分类讨论 对于这种题目,我们可以分类 ...

随机推荐

  1. Hadoop3.1.1架构体系——设计原理阐述与Client源码图文详解 : 总览

    一.设计原理 1.Hadoop架构: 流水线(PipeLine) 2.Hadoop架构: HDFS中数据块的状态及其切换过程,GS与BGS 3.Hadoop架构: 关于Recovery (Lease ...

  2. EnumSet

    这个概念是在 Effective Java中了解到的, 可以通过EnumSet来代替位域这种方式表达.并不是很常见的概念, 因此记录下.如果在这之前恰好了解过 bitmap这种数据结构就更好了.不了解 ...

  3. Mysql查看连接数(连接总数、活跃数、最大并发数)

    show variables like '%max_connection%'; set global max_connections=1000;

  4. 微信小程序传code 拿token 后台报40029 状态吗,是为什么?

    看看是不是code用了两次,还有种可能,检查一下后台的appid

  5. Win2012或Win2016安装网卡

    在电脑安装了Windows Server 2012或者2016等系统之后,可能面临的很大的问题就是没有有线或无线网卡. 1.安装网卡的功能 2.一直到“功能”这部分,选择安装“DirectPlay”和 ...

  6. Java 常见异常及处理方案

    Java 常见异常处理方案 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error: ...

  7. go基础_控制语句

    if控制语句 说明:(1)if后面的条件语句不用加括号 (2)if后面可以跟一个简单的初始化语句,并以分号分割,初始化语句中的变量的作用域是整个if语句块 (3)if语句的条件语句需要尽量简单 (4) ...

  8. 使用JDBC完成分类表CRUD的操作

    工具类 通过之前的案例回顾,不难发现,有很多的代码操作是重复的,比如“获取链接”和“释放资源”等,将来在增删改查中经常遇到,开发中遇到这种情况,将采用工具类的方法进行抽取,从而达到代码的重复利用. 此 ...

  9. 基于Robot Framework的接口自动化测试

    Robot Framework框架简介 Robot Framework框架是一个通用的验收测试和验收测试驱动开发的自动化测试框架(ATDD),使用的是关键字驱动的测试方法.它本身拥有强大的标准库,此外 ...

  10. P&R --From 陌上风骑驴看IC

    FLOORPLAN: 做好floorplan要掌握哪些知识技能 遇到floorplan问题,大致的debug步骤和方法有哪些 如何衡量floorplan的QA 以上是驴神提的五大点问题.鄙人狠狠地反驳 ...