这个东西读完题之后,就能知道我们要逐位计算贡献.
推一下式子,会发现,这一位的贡献,是当前剩余的数字形成的序列的总数,乘上所剩数字中小于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. Siki_Unity_3-8_Lua编程(未完)

    Unity 3-8 Lua编程 任务1&2&3:前言 课程内容: Lua从入门到掌握 为之后的xLua和其他热更新方案打下基础 任务4:Lua简介 Lua是轻量小巧的脚本语言--无需编 ...

  2. PHPCMS的产品筛选功能

    如下图所示功能: 首先,用下面这些代码替换掉phpcms/libs/functions/extention.func.php的内容 <?php /** * extention.func.php ...

  3. Python 内置函数介绍

    作者博文地址:http://www.cnblogs.com/spiritman/ Python Built-in Functions

  4. [笔记] FreeBSD使用小技巧

    非交互式添加用户 sed直接修改文件 sed -i '' 's/a/b/' file sed添加一行 sed '1a\ newline' file sed '1s/.*/&\'$'\nnewl ...

  5. Python20-Day01

    简述编译型与解释型语言的区别,且分别列出你知道的哪些语言属于编译型,哪些属于解释 编译型语言是一种以编译器来实现的编程语言,优缺点:执行速度快,调试麻烦 编译型语言:Java,Go,C,C++ 解释性 ...

  6. 1.12Linux下软件安装(学习过程)

    实验介绍 介绍 Ubuntu 下软件安装的几种方式,及 apt,dpkg 工具的使用. 一.Linux 上的软件安装 通常 Linux 上的软件安装主要有三种方式: 在线安装 从磁盘安装deb软件包 ...

  7. 第三周pspo过程文档

    团队协作:     日期/任务      听课        编写程序         阅读相关书籍 日总计          周一      110          60             ...

  8. erlang调优方法

    1. 来自Scaling Erlang的方法 内核调优: # Increase the ipv4 port range: sysctl -w net.ipv4.ip_local_port_range= ...

  9. Redis的sentinel机制(sentinel节点IP为:192.168.23.10) “哨兵”

    万一主节点打击,主从模型将会停止工作,为了解决这个问题,Redis提供了一个sentinel(哨兵),以此来实现主从切换的功能,一旦主节点宕机了,sentinel将会在从节点中挑一个作为主节点.与zo ...

  10. 复利计算C语言转java的相关代码

    static void principal()// 计算本金 { int N, m; double i, F, P; System.out.printf("复利终值:"); F = ...