取模这种东西到底还是好,将广大OIER从高精度的深渊中解放出来
但是,取模也不总是那么方便的
比方说,我们的操作中不可避免地出现了除法
大部分时候题目会告诉我们取模的数是质数,或者至少除数和取模的数互质,这样我们就能用欧拉定理来解决这个问题了
然而,不幸的事终还是发生了
这道题所要求的取模的数就是任意的~~
题目本身不难,从后往前按位统计+树状数组即可,但由于可重排列公式中不可避免地出现了除法,怎么办?
盾哥从这个题的标程中抠出了下面这个不太优美的方法
先将要取模的数分解质因数,然后每次要乘或要除一个数的时候,就将那个数的质因子中跟取模的数的相同的提出来,这部分因子“暴力”(就是有除法就直接约分,要用的时候再快速幂算值),另外的部分就和要取模的数互质了,可以直接求其乘法逆元
由于一个数的质因数个数是log级别的,所以通过一大堆预处理可以让取模过程均摊O(logN)
可是,
我想了一个比较有效的常数优化,就是算一个数的乘法逆元可以“记忆化”,加入这个优化程序能快上将近一倍
感觉算法不太优美,求更漂亮的实现~~
代码:
program syj;
const
maxn=300005;
type
arr=array[0..43]of longint;
var
n,max,i,j,k,phm,mo,dt:longint; s,ans:int64;
a,b,c,sh,ne,u:array[0..maxn]of longint;
ph:array[0..maxn]of longint;
d:array[0..43]of longint;
z:array[0..43,0..maxn]of longint;
f,g:arr;
procedure update(i:longint);
begin
while i<=max do begin
inc(b[i]);inc(i,i and-i);
end;
end;
procedure ask(i:longint;var s:longint);
begin
s:=0;
while i>0 do begin
inc(s,b[i]);dec(i,i and-i);
end;
end;
function calc(i:longint):longint;
procedure mul(n:longint);
begin
if n=1 then ph[i]:=i else begin
mul(n>>1);
ph[i]:=int64(ph[i])*ph[i] mod mo;
if odd(n) then ph[i]:=int64(ph[i])*i mod mo;
end;
end;
begin
if ph[i]>0 then exit(ph[i]);
mul(phm);
calc:=ph[i];
end;
function work(i,j:longint;var f:arr):int64;
var k:longint;
begin
if i=0 then exit(0);
work:=1;
while i>1 do begin
k:=sh[i];
if u[k]>0 then inc(f[u[k]],j) else
if j>0 then work:=work*k mod mo
else work:=work*calc(k) mod mo;
i:=ne[i];
end;
end;
begin
assign(input,'per.in');reset(input);
assign(output,'per.out');rewrite(output);
readln(n,mo);
k:=mo;phm:=1;
for i:=2 to trunc(sqrt(k)) do
if k mod i=0 then begin
inc(dt); d[dt]:=i; u[i]:=dt; j:=1;
while k mod i=0 do begin
k:=k div i;j:=j*i;
end;
phm:=phm*(j div i*(i-1));
end;
if k>1 then phm:=phm*(k-1);
dec(phm,1);
if (k>0)and(k<=n) then begin
inc(dt);d[dt]:=k;u[k]:=dt;
end;
for i:=1 to dt do begin
z[i,0]:=1;
for j:=1 to n do z[i,j]:=int64(z[i,j-1])*d[i] mod mo;
end;
for i:=1 to n do begin
read(a[i]);
if a[i]>max then max:=a[i];
end;
if n>max then max:=n;
for i:=2 to max do
for j:=1 to max div i do
if sh[i*j]=0 then begin
sh[i*j]:=i; ne[i*j]:=j;
end;
s:=1; ans:=1;
for i:=n downto 1 do begin
ask(a[i]-1,k);
fillchar(g,sizeof(g),0);
j:=work(k,1,g);
inc(c[a[i]]);
s:=s*work(c[a[i]],-1,f) mod mo;
j:=s*j mod mo;
if j>0 then
for k:=1 to dt do
j:=int64(j)*z[k,g[k]+f[k]] mod mo;
inc(ans,j);
update(a[i]);
s:=s*work(n-i+1,1,f) mod mo;
end;
writeln(ans mod mo);
close(input);close(output);
end.
速度比盾哥快,哈哈~