POIXV Permutation
Description
Multiset is a mathematical object similar to a set, but each member of a multiset may have more than one membership. Just as with any set, the members of a multiset can be ordered in many ways. We call each such ordering a permutation of the multiset. For example, among the permutations of the multiset\((1,1,2,3,3,3,7,8)\) there are\((2,3,1,3,3,7,1,8)\) and\((8,7,3,3,3,2,1,1)\) .
We will say that one permutation of a given multiset is smaller (in lexicographic order) than another permutation, if on the first position that does not match the first permutation has a smaller element than the other one. All permutations of a given multiset can be numbered (starting from one) in an increasing order.
Write a programme that
- reads the description of a permutation of a multiset and a positive integer \(m\) from the standard input,
- determines the remainder of the rank of that permutation in the lexicographic ordering modulo \(m\),
- writes out the result to the standard output.
Input
The first line of the standard input holds two integers \(N\) and \(M\) \((1 \le N \le 3 \times 10^5, 2 \le m \le 10^9)\), separated by a single space. These denote, respectively, the cardinality of the multiset and the number \(m\). The second line of the standard input contains \(n\) positive integers \(a_i\) \((1 \le a_i \le 3 \times 10^5)\), separated by single spaces and denoting successive elements of the multiset permutation.
Output
The first and only line of the standard output is to hold one integer, the remainder modulo of the rank of the input permutation in the lexicographic ordering.
Sample Input
4 1000
2 1 10 2
Sample Output
5
首先我们考虑没有重复的元素的排列,则其序数(从\(0\)开始)为
\]
其中\(r_i\)表示\(a_i\)在未在排列前\(i-1\)位出现的元素的排名。这就是康托展开,用树状数组可以将复杂度优化到\(O(nlogn)\)。
下面我们考虑有重集合,用康托展开一样的方式思考。我们可以得出
\]
其中\(j\)表示除去排列前\(i-1\)位的元素还剩的元素,\(f_{i,j}\)表示确定了前\(i-1\)位后第\(i\)位放\(j\)的所有可能的排列个数。用可重排列公式,不难得出
\]
\(c_k\)表示确定了前\(i-1\)位后可重集合中\(k\)这个数的个数。
那么问题就来了,我们怎么去计算这个式子呢?
其实还是可以用树状数组来维护的。
首先有$$f_{i,j} = c_j\frac{(N-i)!}{\prod_{k = 1}^{3 \times 10^5}c_k}$$
所以
\]
然后没移动\(i\)一次,只会修改一个\(c_j\),于是复杂度还是\(O(nlogn)\)。
但是还有一个问题——\(M\)不一定时素数。我们可以将其分解质因数
\]
然后我们只要能够计算出\(ans\)在模\(M_i = p_i^{d_i}\)的值,再通过中国剩余定理就可以计算答案了。那么这个怎么求呢?
我们可以将每个数字\(x\)用个二元组\((s,t)\)来表示,\(x = s \times p_i^t\),且\((s,p_i) = 1\)。
于是有
- \((s,t) \times (u,v) = (s \times u,t+v)\)
- \((s,t) / (u,v) = (s \times u^{-1},t-v)\)
\(u^{-1}\)表示\(u\)在模\(M_i\)下的逆元。由此可以看出该二元组的第一关键字可以是模\(M_i\)意义下的。用此方法处理乘除。
当涉及到加法的时候将二元组转换成普通数即可。
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef long long ll;
#define lowbit(a) (a&(-a))
#define maxn (300010)
int mod,ans,N,M,tot,A[maxn],aim[maxn],Mi[maxn],Pi[maxn],res[maxn],tree[maxn],num[maxn],tnum[maxn];
inline ll exgcd(ll a,ll b,ll c)
{
if (!a) return -1;
else if (!(c % a)) return c/a;
ll t = exgcd(b % a,a,((-c % a)+a)%a);
if (t == -1) return -1;
return (t*b+c)/a;
}
inline ll qsm(ll a,int b,int c)
{
ll ret = 1;
for (;b;b >>= 1,(a *= a) %= c) if (b & 1) (ret *= a) %= c;
return ret;
}
struct node
{
int a,b;
inline node(int x = 0,int p = 0) { if (!p) return; b = 0; while (!(x % p)) ++b,x /= p; a = x%mod; }
friend inline node operator * (const node &x,const node &y)
{
node ret;
ret.a = (ll)x.a*(ll)y.a%mod; ret.b = x.b+y.b;
return ret;
}
friend inline node operator / (const node &x,const node &y)
{
node ret; int inv = exgcd(y.a,mod,1)%mod;
ret.a = (ll)x.a*(ll)inv%mod; ret.b = x.b-y.b;
return ret;
}
inline int tran(int p) { return (ll)a*qsm(p,b,mod)%mod; }
};
inline void ins(int a,int b) { for (;a <= 300000;a += lowbit(a)) tree[a] += b; }
inline int calc(int a) { int ret = 0; for (;a;a -= lowbit(a)) ret += tree[a]; return ret; }
inline void Div(int key)
{
for (int i = 2;i*i <= key;++i)
if (key % i == 0)
{
Mi[++tot] = 1; Pi[tot] = i;
while (key % i == 0) Mi[tot] *= i,key /= i;
}
if (key > 1) Mi[++tot] = key,Pi[tot] = key;
}
inline void init()
{
memset(tree,0,sizeof(tree)); memcpy(tnum,num,sizeof(num));
for (int i = 1;i <= 300000;++i) if (num[i]) ins(i,num[i]);
}
inline void work(int id)
{
init(); mod = Mi[id]; node now(1,Pi[id]);
for (int i = 1;i < N;++i)
{
node tmp(i,Pi[id]);
now = now*tmp;
}
for (int i = 1;i <= 300000;++i)
for (int j = 2;j <= num[i];++j) { node tmp(j,Pi[id]); now = now/tmp; }
for (int i = 1,sum;i <= N;++i)
{
if (sum = calc(A[i]-1)) res[id] += (now*node(sum,Pi[id])).tran(Pi[id]);
if (res[id] >= mod) res[id] -= mod; ins(A[i],-1);
if (i < N)
{
node tmp1(N-i,Pi[id]),tmp2(tnum[A[i]]--,Pi[id]);
now = now*tmp2/tmp1;
}
}
}
inline int crt()
{
int ret = 0;
for (int i = 1;i <= tot;++i)
{
int tm = M/Mi[i],inv = exgcd(tm%Mi[i],Mi[i],1)%Mi[i];
ret += ((ll)res[i]*(ll)inv%M*(ll)tm)%M;
if (ret >= M) ret -= M;
}
return ret;
}
int main()
{
// freopen("permutation.in","r",stdin);
// freopen("permutation.out","w",stdout);
scanf("%d %d",&N,&M);
for (int i = 1;i <= N;++i) scanf("%d",A+i);
Div(M);
for (int i = 1;i <= N;++i) ++num[A[i]];
for (int i = 1;i <= tot;++i) work(i);
ans = crt(); if (++ans >= M) ans -= M;
printf("%d",ans);
// fclose(stdin); fclose(stdout);
return 0;
}
POIXV Permutation的更多相关文章
- Permutation Sequence
The set [1,2,3,-,n] contains a total of n! unique permutations. By listing and labeling all of the p ...
- [LeetCode] Palindrome Permutation II 回文全排列之二
Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empt ...
- [LeetCode] Palindrome Permutation 回文全排列
Given a string, determine if a permutation of the string could form a palindrome. For example," ...
- [LeetCode] Permutation Sequence 序列排序
The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the p ...
- [LeetCode] Next Permutation 下一个排列
Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...
- Leetcode 60. Permutation Sequence
The set [1,2,3,-,n] contains a total of n! unique permutations. By listing and labeling all of the p ...
- UVA11525 Permutation[康托展开 树状数组求第k小值]
UVA - 11525 Permutation 题意:输出1~n的所有排列,字典序大小第∑k1Si∗(K−i)!个 学了好多知识 1.康托展开 X=a[n]*(n-1)!+a[n-1]*(n-2)!+ ...
- Permutation test: p, CI, CI of P 置换检验相关统计量的计算
For research purpose, I've read a lot materials on permutation test issue. Here is a summary. Should ...
- Permutation
(M) Permutations (M) Permutations II (M) Permutation Sequence (M) Palindrome Permutation II
随机推荐
- Windows下安装Redmine-2.5.3
安装准备 服务器操作系统:Windows Server 2008 R2 Standard,64位操作系统. RailsInstaller版本:2.2.4 (下载地址http://railsinsta ...
- Javaweb 第2天 JavaScript课程
JavaScript课程 两日大纲 ● JavaScript基本语法 ● JavaScript函数 ● JavaScript基于对象编程 ● JavaScript BOM对象编程 ● JavaScri ...
- response.addCookie(cookie)添加cookie失败.
两个if循环能进来,创建的两个cookie也能通过控制台输出. 但是却添加失败. 原因是:request.getRequestDispatcher("/MainFrame").f ...
- php常量作用
PHP常量的特点是: 1.不能通过外部来传递,也就是不能通过get与post还有cookie等方式来传递赋值,所以可以确定是程序执行过程中定义的,非外界变量.2.定义后不能更改,可以防止被篡改. 基于 ...
- 在ubuntu14.04上安装openstack mitaka
最近在工作环境安装部署了juno版本,在GE口测试网络性能不太满意,发现mitaka版本支持ovs-dpdk,于是抽时间安装实验一番. 参考官网的安装文档,先准备将mitaka版本安装好再配置ovs. ...
- Unity3D脚本使用:物体调用物体
如下图4种方式: 方式5 通过Tag定位物体 1.先对物体定义标签Tag,可选已有或自定义 2.通过Tag名称找到对象 注意:如果标签对应多个对象,需使用获取对象集合再进行处理
- hdu 3006 The Number of set
二进制的状态压缩.比如A集合里面有{1,5,7}那么就表示为1010001.B集合有{3,4},二进制表示1100.A|B=1011101. 按照这样的思路 可以用01背包 把所有的组合全部求出来. ...
- Number Sequence (HDU 1711)
Number Sequence Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- tomcat服务配置及搭建
一.在官网上下载tomcat 下载地址:http://tomcat.apache.org/download-60.cgi 下载完后解压 二.设置环境变量 1,JAVA_HOME 2.CATALINA_ ...
- Java 并发 线程的优先级
Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于 ...