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
随机推荐
- ETL的经验总结
ETL的考虑 做数据仓库系统,ETL是关键的一环.说大了,ETL是数据整合解决方案,说小了,就是倒数据的工具.回忆一下工作这么些年来,处理数据迁移.转换的工作倒还真的不少.但是那些工作基 ...
- 删除style的样式JQuery
有些页面样式不规范,没有写在一个class里,例如:<div id="show" style="width:100px; padding-top:10px; f ...
- openstack私有云布署实践【8.2 身份认证keystone的API创建(办公网环境)】
其中一台controller上面加入环境变量,我选controller1,关注的是endpoint的名称不一样,其它创建的参数与生产环境一致 export OS_TOKEN=venicchina ex ...
- 关于 CentOS 自启动(服务、脚本)
/etc/init.d /etc/rc.local 其实是软连接,所以,实际上看 /etc/rc.d/ 这个文件夹就好了 rc.local 是自启动脚本 正常情况下,用户自定义的服务在 init.d ...
- Ubuntu16 64位安装steam, 并解决无法启动的问题
直接用crtl+shift打开终端,运行下面的命令. sudo add-apt-repository multiverse sudo apt update sudo apt install steam ...
- fileupload实现控制大小进行图片上传
if ($(".img-upload").length > 0) { $('.img-upload').fileupload({ type: 'POST', url: &qu ...
- usaco 2.2.4 生日派对灯(最近写题碰到的,虽然知道现在写这个有点晚了)
经过分析,他看似很多的开灯的方法其实合并起来就只有八个. 首先,一个开关在执行的时候只能按一次(因为你就算按了两次就相当于一次也没有按). 当一个都不按的时候 当然就只有一种:不按. 当按一下的时候 ...
- phonegap 随笔
开发者论坛 http://bbs.phonegapcn.com/forum.php phone调用android本地方法 http://blog.csdn.net/crazyman2010/artic ...
- CodeForces 687B Remainders Game
数论. 如果$x$不唯一,假设存在两个解,较大的为${x_1}$,较小的为${x_2}$. 那么, $\left\{ {\begin{array}{*{20}{c}}{{x_1}\% {c_i} = ...
- Ubuntu 16.04上Docker使用手记
一.Docker Hub的使用Docker Hub是Docker官方维护的仓库,里面已经包含了很多的镜像,一般我们的需求直接在官方仓库搜索就可以得到解决.在官方的公共仓库中我们无需登录就可以进行镜像的 ...