这个东西读完题之后,就能知道我们要逐位计算贡献.
推一下式子,会发现,这一位的贡献,是当前剩余的数字形成的序列的总数,乘上所剩数字中小于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. JY播放器【蜻蜓FM电脑端,附带下载功能】

    今天给大家带来一款神器----JY播放器.可以不用打开网页就在电脑端听蜻蜓FM的节目,而且可以直接下载,对于我这种强迫症患者来说真的是神器.我是真的不喜欢电脑任务栏上面密密麻麻. 目前已经支持平台(蜻 ...

  2. js判断PC端 移动端 并跳转到对应页面

    一.PC端跳转到移动端 html页面: <script>var webroot="/",catid="{$catid}",murl="m/ ...

  3. sql脚本创建用户角色权限表

    /******************************************/ /* 名称:权限管理 */ /* 编写日期:2010.5.20 */ /* 开发者:dangqinghua * ...

  4. ThinkPHP - 6 - 学习笔记(2015.5.4)

    解决:OneThink 站点无法被友言uyan后台识别 打开友言uyan插件功能,但OneThink站点无法被友言uyan后台检测到.页面生成的uyan代码为: <!-- UY BEGIN -- ...

  5. Appstate的几种状态及在android 和ios触发

    AppState能告诉你当前应用是在前台还是在后台,或者处于切换应用的状态,并且能在状态变化的时候通知你. AppState 通常在处理推送通知的时候用来决定内容和对应的行为 一: App State ...

  6. eclipse技巧-快捷键

    ctrl + 1,快速修复 ctrl + d, 快捷删除行 shift + Enter,快速移动光标到下一行 ctrl + F11,运行代码 alt + ↑/↓,快速移动行 ctrl + alt + ...

  7. Python坑系列:可变对象与不可变对象

    在之前的文章 http://www.cnblogs.com/bitpeng/p/4748148.html 中,大家看到了ret.append(path) 和ret.append(path[:])的巨大 ...

  8. Scrum立会报告+燃尽图(十月二十三日总第十四次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2246 项目地址:https://git.coding.net/zhang ...

  9. 《JavaScript》页面跳转

    window.location.href: <i onclick="window.location.href = '/Form/Form_Write/Index?viewname=Fo ...

  10. mysql-otp 驱动中设置utf8mb4

    utf8mb4支持emoji表情,在mysql中设置连接字符集为utf8mb4可以直接储存emoji表情. 可以在客户端连接中设置: SET NAMES utf8mb4 查看是否起效: SHOW VA ...