洛谷 P3477 [POI2008]PER-Permutation 解题报告
P3477 [POI2008]PER-Permutation
题目描述
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.
Task Write a programme that reads the description of a permutation of a multiset and a positive integerm 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.
多重集合是数学中的一个概念,它的定义很像集合,但是在多重集之中,同一个元素可以出现多次。
和集合一样,多重集的的元素可以有很多种元素的排布顺序。我们把它叫作多重集的排列。
现在我们定义多重集的某个排列\(s_i\)比某个排列\(s_j\)的大小比较为字典序比较。这样某个多重集的排列可以从小到大得排起来。
现在给你一个元素个数为n的多重集的一个排列和\(m\),求这个排列的排名取模\(m\)。
输入输出格式
输入格式:
The first line of the standard input holds two integers n( \(1\le n \le 300000\)) and m ( \(2 \le m \le 10^9\)) ,separated by a single space. These denote, respectively, the cardinality of the multiset and \dots\ the number m.
The second line of the standard input contains n positive integers \(a_i\) (\(1\le a_i \le 300000\)), separated by single spaces and denoting successive elements of the multiset permutation.
第一行 两个整数n,m
第二行 n个数,代表多重集的排列
输出格式:
The first and only line of the standard output is to hold one integer, the remainder modulo m of the rank of the input permutation in the lexicographic ordering.
一行一个整数 排名取模m
一句话题意:求有重复元素的排列的排名,模数不一定是质数
我们找到字典序比它小的排列的个数
讨论每一位数值的贡献,像康托展开那样
设给出排列为 \(a_1,a_2,a_3,...a_n\),字典序比它小的排列为\(b_1,b_2,b_3,...b_n\)
考虑从左到右第\(i\)位的贡献
若\(b_i=a_i\) 右边的是子问题
若\(b_i<a_i\),则设右边的从小到大每个元素出现的次数分别为\(c_1,c_2,...,c_m\)
若\(b_i\)出现\(c_j\)次
则当\(b_i\)这个数值放在第\(i\)位置,右边的全排列的贡献为
\(\frac{(n-i)!}{c_1! \times c_2 ! \times ... \times c_m!} \times c_j\)
则所有可以放到第一位的数的贡献和为
\(\frac{(n-i)!}{c_1! \times c_2 ! \times ... \times c_m!} \times \sum_{b_i<a_i}c_j\)(\(b_i\)这里代表数值意义,一个数值只出现一次)
我们处理每一位这样的
后面的好处理,树状数组维护一下就行了
前面的因为模数不为质数可能没有逆元,所有我们先把模数唯一分解,对每一个质因子的多少次方做,然后CRT进行合并
在某个质因子多少次方下做时,我们把数字拆成 其他项 和 这个质因子多少次方 就行了
因为答案一定是整数,所以分子的质因子个数一定大于分母的,我们把这些先拿出来,就可以求逆元啦,然后快速幂乘回去就行
注意小细节
Code:
#include <cstdio>
#include <cstring>
#define ll long long
const int N=3e5+10;
ll a[N],m,n,buct[N],id[N],cnt0[N];
ll fac[N],ct[N],mx,sum[N],mod;
void exgcd(ll a,ll b,ll &x,ll &y)//只是一个exgcd..
{
if(!b){x=1,y=0;return;}
exgcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-a/b*y;
}
ll inv(ll a,ll p)//只是一个求逆元
{
ll x,y;
exgcd(a,p,x,y);
return (x%p+p)%p;
}
ll CRT(ll a,ll b)//只是CRT的一项
{
return mod/b*a%mod*inv(mod/b,b)%mod;
}
ll quick_pow(ll d,ll k,ll p)//只是一个快速幂
{
ll f=1;
while(k)
{
if(k&1) f=f*d%p;
d=d*d%p;
k>>=1;
}
return f;
}
ll query(ll x)
{
ll s;for(s=0;x;x-=x&-x) s+=sum[x];
return s;
}
void add(ll x)
{
while(x<=m) ++sum[x],x+=x&-x;
}
ll cal(ll d,ll p)
{
ll ans=0;fac[0]=1;
memset(ct,0,sizeof(ct));
for(ll i=1;i<=mx;i++)
{
ll num=i;
while(num%d==0) num/=d;
(fac[i]=fac[i-1]*num%p)%=p;
for(ll j=i;j;j/=d) ct[i]+=j/d;
}
memset(cnt0,0,sizeof(cnt0));
memset(sum,0,sizeof(sum));
cnt0[a[n]]++;add(a[n]);
ll den=1,cn=0;
for(ll i=n-1;i;i--)
{
ll cc=++cnt0[a[i]];
add(a[i]);
if(cc>1)
{
(den*=fac[cc-1]*inv(fac[cc],p)%p)%=p;
cn+=ct[cc]-ct[cc-1];
}
ll k=query(a[i]-1);
(ans+=fac[n-i]*den%p*k%p*(k?quick_pow(d,ct[n-i]-cn,p):0)%p)%=p;
}
return ans;
}
ll calp(ll p)
{
ll ans=0;
fac[0]=1;
for(ll i=1;i<=mx;i++)
fac[i]=(fac[i-1]*i)%p;
memset(cnt0,0,sizeof(cnt0));
memset(sum,0,sizeof(sum));
cnt0[a[n]]++;add(a[n]);
ll den=1;
for(ll i=n-1;i;i--)
{
ll cc=++cnt0[a[i]];
add(a[i]);
if(cc>1) (den*=fac[cc-1]*inv(fac[cc],p)%p)%=p;
(ans+=fac[n-i]*den%p*query(a[i]-1)%p)%=p;
}
return ans;
}
ll work(ll p)
{
ll ans=0;
for(ll i=2;i*i<=p;i++)
{
if(p%i==0)
{
ll d=1;
while(p%i==0)
p/=i,d*=i;
if(i==d)
(ans+=CRT(calp(i)+1,p))%=mod;
else
(ans+=CRT(cal(i,d)+1,d))%=mod;
}
}
if(p!=1) (ans+=CRT(calp(p)+1,p))%=mod;
return ans;
}
int main()
{
//freopen("data.in","r",stdin);
scanf("%lld%lld",&n,&mod);
for(ll i=1;i<=n;i++) scanf("%lld",a+i),buct[a[i]]++;
for(ll i=1;i<=N-10;i++) if(buct[i]) id[i]=++m;
for(ll i=1;i<=n;i++)
{
mx=mx>buct[a[i]]?mx:buct[a[i]];
a[i]=id[a[i]];
}
mx=mx>n?mx:n;
printf("%lld\n",work(mod));
return 0;
}
2018.8.27
洛谷 P3477 [POI2008]PER-Permutation 解题报告的更多相关文章
- 洛谷_Cx的故事_解题报告_第四题70
1.并查集求最小生成树 Code: #include <stdio.h> #include <stdlib.h> struct node { long x,y,c; ...
- 洛谷 P2317 [HNOI2005]星际贸易 解题报告
P2317 [HNOI2005]星际贸易 题目描述 输入输出格式 输入格式: 输出格式: 如果可以找到这样的方案,那么输出文件output.txt中包含两个整数X和Y.X表示贸易额,Y表示净利润并且两 ...
- 洛谷 P3802 小魔女帕琪 解题报告
P3802 小魔女帕琪 题目背景 从前有一个聪明的小魔女帕琪,兴趣是狩猎吸血鬼. 帕琪能熟练使用七种属性(金.木.水.火.土.日.月)的魔法,除了能使用这么多种属性魔法外,她还能将两种以上属性组合,从 ...
- 洛谷 P2606 [ZJOI2010]排列计数 解题报告
P2606 [ZJOI2010]排列计数 题目描述 称一个\(1,2,...,N\)的排列\(P_1,P_2...,P_n\)是\(Magic\)的,当且仅当对所以的\(2<=i<=N\) ...
- 洛谷1303 A*B Problem 解题报告
洛谷1303 A*B Problem 本题地址:http://www.luogu.org/problem/show?pid=1303 题目描述 求两数的积. 输入输出格式 输入格式: 两个数 输出格式 ...
- 洛谷 P3084 [USACO13OPEN]照片Photo 解题报告
[USACO13OPEN]照片Photo 题目描述 农夫约翰决定给站在一条线上的\(N(1 \le N \le 200,000)\)头奶牛制作一张全家福照片,\(N\)头奶牛编号\(1\)到\(N\) ...
- 洛谷 P1379 八数码难题 解题报告
P1379 八数码难题 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初 ...
- NOIP2015 D2T3 洛谷2680 BZOJ4326 运输计划 解题报告
前言:个人认为这是历年NOIP中比较简单的最后一题了,因此将自己的思路与大家分享. 题目大意: 给一棵无根树,给出m条路径.允许将树上的一条边的权值改为0.求m条路径长度最大值的最小值.n,m< ...
- 洛谷 P1129 [ZJOI2007]矩阵游戏 解题报告
P1129 [ZJOI2007]矩阵游戏 题目描述 小\(Q\)是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏.矩阵游戏在一个\(N*N\)黑白方阵进行(如同国际象棋一般 ...
随机推荐
- MySQL多表数据查询(DQL)
数据准备: /* ------------------------------------创建班级表------------------------------------ */ CREATE TAB ...
- NOIP模拟 candy
题目描述 一天,小 DD 决定买一些糖果.他决定在两家不同的商店中买糖果,来体验更多的口味. 在每家商店中都有 nn 颗糖果,每颗糖果都有一个权值:愉悦度,代表小 DD 觉得这种糖果有多好吃.其中,第 ...
- vi/vim连续注释
知识点: 1-可视块模式方法 2-替换方法 3-自定义快捷键方式 今天刚好重新在linux上手工搭建完Lamp环境,用来下vi操作,一段时间不用就有些生疏了,正好经常要注释,回顾下自己会的方法,小结一 ...
- 初学tiny4412
1.解压友善之臂提供的uboot make tiny4412_config make 然后将sd卡插到电脑上,编辑虚拟机,选择对应的usb口(usb3.0兼容),如果没有usb3.0,可能是虚拟机版本 ...
- Scrapy框架的基本使用
安装 pip install scrapy 基础使用 1. 创建一个工程:scrapy startproject 2. 在工程目录下创建一个爬虫文件 cd 工程 scrapy genspider 爬虫 ...
- POJ1236 tarjan
Network of Schools Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 19613 Accepted: 77 ...
- Java+Selenium3方法篇24-单选和多选按钮操作
Java+Selenium3方法篇24-单选和多选按钮操作 本篇介绍 webdriver处理前端单选按钮的操作.单选按钮一般叫raido button,就像我们在电子版的单选答题过程一样,单选只能点击 ...
- 安装 Node.js v8.0 生产环境
步骤://center os 上把apt-get换成yum 第一步:进入服务器升级工具包 sudo apt-get update 第二步:安装git sudo apt-get install git ...
- rhel6.4扩充swap分区
状况:Red hat 6.4 swap分区不足 解决:扩充swap ================================================================== ...
- HDOJ 1176 免费馅饼(完全背包)
参考:https://blog.csdn.net/hhu1506010220/article/details/52369785 https://blog.csdn.net/enjoying_scien ...