这个东西读完题之后,就能知道我们要逐位计算贡献.
推一下式子,会发现,这一位的贡献,是当前剩余的数字形成的序列的总数,乘上所剩数字中小于s上这一位的数的个数与所剩数字的总数的比.
所以我们维护“当前剩余的数字形成的序列的总数”以及权值数组的前缀和就好了.
后者可以用树状数组维护,前者可以用一个变量维护.
但是!!!!!模数不是质数!!!!!
这就很坑爹,网上的人基本上都是把模数质因数分解后,对于每一种质因数计算一次答案,最后再crt计算答案.
然而,作为一只**,我用长得像二叉堆,但是维护信息上又有点像平衡树的二叉树维护变量,直接以m为模数计算出答案.
我的思路是这样的,我们维护数字,只有乘和除,既然不能算逆元,那么我们就维护这个数字的所有可能含有的质数的个数,并且对于所有的质数建立树形结构,每个点除了维护其自己信息以外,还维护了其子树乘积,那么树根的子树乘积就是这个数,而我们乘(除)一个数的时候,将乘(除)的数质因子拆分,对于每个质因子,修改他在树中的信息以及他的在树中的祖先的信息,这样的复杂度是O(nlog^2n)的.
一开始我直接按照质数的大小建立BST,卡了半天常数才过,后来发现,我不如按照质数大小建立小根堆,这样使用频繁的质数(小的质数)的深度就会变小,于是我一下子从bzoj倒数第一滚到大众时间.

#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register
typedef long long LL;
char xB[(<<)+],*xS,*xT;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){
R char ch=gtc;
for(x=;ch<''||ch>'';ch=gtc);
for(;ch>=''&&ch<='';x=(x*)+ch-'',ch=gtc);
}
const int N=;
inline int gcd(int x,int y){return !x?y:gcd(y%x,x);}
int len,prime[N/+],min[N+],size[N/+];
bool isnot[N+];
int n,m,ans,P,lim;
int mii[N*+],*begin[N/+];
#define mi(a,b) (*(begin[(a)]+(b)))
int a[N+],tree[N+],cnt[N+];
inline void U(R int pos,int key){
for(;pos<=N;pos+=pos&(-pos))
tree[pos]+=key;
}
inline int Q(R int pos){
R int ret=;
for(;pos>;pos-=pos&(-pos))
ret+=tree[pos];
return ret;
}
inline void get(R int x,int opt){
while(min[x])
size[min[x]]+=opt,x/=prime[min[x]];
}
struct BST{
BST *ch[],*f;
int id,key;
}node[N+],*root;
#define pushup(p) (p->key=(LL)mi(p->id,size[p->id])*p->ch[0]->key%P*p->ch[1]->key%P)
#define mid ((l+r)>>1)
inline void build(BST *&p,BST *fa,int id){
p=node+id,p->key=;
if(id>len)return;
p->f=fa,p->id=id;
build(p->ch[],p,id<<);
build(p->ch[],p,(id<<)|);
pushup(p);
}
inline void update(int x){
R BST *p=node+x;
while(p)pushup(p),p=p->f;
}
inline void update(R int x,int opt){
if(x==)return;
R int last=;
while(min[x]){
size[min[x]]+=opt;
if(last&&min[x]!=last)update(last);
last=min[x];
x/=prime[min[x]];
}
update(last);
}
int main(){
//freopen("rio.in","r",stdin);
R int i,j;
read(n),read(P),lim=n;
isnot[]=true,min[]=;
for(i=;i<=lim;++i){
if(!isnot[i])prime[++len]=i,min[i]=len;
for(j=;prime[j]*i<=lim;++j){
isnot[prime[j]*i]=true;
min[prime[j]*i]=j;
if(i%prime[j]==)break;
}
}
for(i=;i<=n;++i)read(a[i]),U(a[i],),++cnt[a[i]];
for(i=;i<=n;++i)get(i,);
for(i=;i<=len;++i){
begin[i]=mii+m;
m+=size[i]++;
mi(i,)=;
for(j=;j<=size[i]+;++j)
mi(i,j)=(LL)mi(i,j-)*prime[i]%P;
}
for(i=;i<=N;++i)
for(j=;j<=cnt[i];++j)
get(j,-);
build(root,NULL,);
R int s;
for(i=;i<=n;++i){
s=Q(a[i])-cnt[a[i]];
if(s){
update(s/gcd(s,n-i+),),update((n-i+)/gcd(s,n-i+),-);
ans=(ans+root->key)%P;
update(cnt[a[i]],),update(s,-);
}else{
update(cnt[a[i]],),update(n-i+,-);
}
--cnt[a[i]],U(a[i],-);
}
ans=(ans+)%P;
printf("%d\n",ans);
return ;
}

【BZOJ 1129】[POI2008]Per 二叉堆的更多相关文章

  1. BZOJ 4241: 历史研究——莫队 二叉堆

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4241 题意:N个int范围内的数,M次询问一个区间最大的(数字*出现次数)(加权众数),可以 ...

  2. AC日记——二叉堆练习3 codevs 3110

    3110 二叉堆练习3  时间限制: 3 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 给定N(N≤500,000)和N个整 ...

  3. codevs 3110 二叉堆练习3

    3110 二叉堆练习3 http://codevs.cn/problem/3110/ 题目描述 Description 给定N(N≤500,000)和N个整数(较有序),将其排序后输出. 输入描述 I ...

  4. 数据结构图文解析之:二叉堆详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  5. POJ 2010 - Moo University - Financial Aid 初探数据结构 二叉堆

    考虑到数据结构短板严重,从计算几何换换口味= = 二叉堆 简介 堆总保持每个节点小于(大于)父亲节点.这样的堆被称作大根堆(小根堆). 顾名思义,大根堆的数根是堆内的最大元素. 堆的意义在于能快速O( ...

  6. 二叉堆(一)之 图文解析 和 C语言的实现

    概要 本章介绍二叉堆,二叉堆就是通常我们所说的数据结构中"堆"中的一种.和以往一样,本文会先对二叉堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本 ...

  7. 二叉堆(二)之 C++的实现

    概要 上一章介绍了堆和二叉堆的基本概念,并通过C语言实现了二叉堆.本章是二叉堆的C++实现. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的C++实现(完整源码)4. 二叉堆的C++测试程 ...

  8. 二叉堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了二叉堆,本章给出二叉堆的Java版本.还是那句话,它们的原理一样,择其一了解即可. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的Java实现(完整源码) ...

  9. 二叉堆(binary heap)

    堆(heap) 亦被称为:优先队列(priority queue),是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因 ...

随机推荐

  1. 180729-Quick-Task 动态脚本支持框架之任务动态加载

    Quick-Task 动态脚本支持框架之任务动态加载 前面几篇博文分别介绍了整个项目的基本架构,使用说明,以及整体框架的设计与实现初稿,接下来则进入更细节的实现篇,将整个工程中核心实现捞出来,从为什么 ...

  2. google::protobuf 编译方法

    这两天用了一下Protobuf 感觉很方便, 记录一下编译过程, 以做务忘(需要安装cmake): 1: 下载地址: https://developers.google.com/protocol-bu ...

  3. Appium+python HTML测试报告(1)(转)

    (原文:https://www.cnblogs.com/fancy0158/p/10054632.html) 测试任务执行完成后,我们需要一份通俗易懂的测试报告来展示自动化测试的结果. HTMLTes ...

  4. Appium+python的单元测试框架unittest(2)——fixtures(转)

    (原文:https://www.cnblogs.com/fancy0158/p/10046333.html) unittest提供的Fixtures用以在测试执行前和执行后进行必要的准备和清理工作,可 ...

  5. selenium 各种很奇葩的异常

    问题1:使用selenium3+java的脚本模拟登陆时,总是提示用户名,密码错误 解决方法:1 在执行输入用户名和密码的代码之前,加上driver.navigate().refresh(); QQ群 ...

  6. HDU-4055:Number String

    链接:HDU-4055:Number String 题意:给你一个字符串s,s[i] = 'D'表示排列中a[i] > a[i+1],s[i] = 'I'表示排列中a[i] < a[i+1 ...

  7. MySQL事务、并发问题、锁机制

    MySQL事务,并发问题,锁机制 1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库 ...

  8. 模拟IDE上的run过程

    看了一下老陈写的模仿JDK动态代理,从中取一部分单独扩展,模拟一下IDE上的run过程(不愧是老陈,去年写的东西我要现在才能理解) 对run过程的猜想 在点击run的过程中应该做了不少事.先编译运行r ...

  9. Vue 项目在其他电脑 npm run dev 运行报错的解决方法

    一个 Vue 项目从一台电脑上传到 github 上之后,再另外一台电脑上 git clone .并使用 npm run dev 或 npm run start 发生以下报错的解决方法.   报错原因 ...

  10. 剑指 Offer——数字在排序数组中出现的次数

    1. 题目 2. 解答 时间复杂度为 \(O(n)\) 的算法,顺序遍历数组,当该数字第一次出现时开始记录次数. class Solution { public: int GetNumberOfK(v ...