BZOJ1129 : [POI2008]Per

枚举LCP,假设前$i-1$个都相同。
那么后面$n-i$个数可以随意排列,第$i$个位置可以填的方案数为后面小于$a_i$的数字个数,树状数组维护。

同时为了保证本质不同,方案数需要除以每个数字的个数的阶乘。

将$m$分解质因数,然后CRT合并即可。

可以先用树状数组处理出所有贡献。

同时在分开计算答案的时候,除了某个超过$\sqrt{m}$的大因子之外,其它模数的逆元都可以线性预处理。

所以总时间复杂度为$O(n\log n)$。

#include<cstdio>
#include<algorithm>
#define N 300010
typedef long long ll;
int n,m,i,a[N],b[N],c[N],bit[N],f[N],g[N],ans,flag,K;ll B,P,x,y,inv[N];
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
inline int lower(int x){
  int l=1,r=n,mid,t;
  while(l<=r)if(b[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1;
  return t;
}
ll exgcd(ll a,ll b){
  if(!b)return x=1,y=0,a;
  ll d=exgcd(b,a%b),t=x;
  return x=y,y=t-a/b*y,d;
}
inline ll rev(ll a){
  if(flag&&a<P)return inv[a];
  exgcd(a,P);
  return (x+P)%P;
}
inline void add(int x,int y){for(;x<=n;x+=x&-x)bit[x]+=y;}
inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
struct Num{
  ll a,b;
  Num(){a=1,b=0;}
  Num(ll _a,ll _b){a=_a,b=_b;}
  Num operator*(const Num&x){return Num(a*x.a%P,b+x.b);}
  Num operator/(const Num&x){return Num(a*rev(x.a)%P,b-x.b);}
  ll val(){
    if(!a||b>=K)return 0;
    ll t=a,x=B,k=b;
    for(;k;k>>=1,x=x*x%P)if(k&1)t=t*x%P;
    return t;
  }
  void set(ll n){
    a=n,b=0;
    while(a%B==0)a/=B,b++;
    a%=P;
  }
}w[N],t;
void solve(ll _B,ll _P,int _K){
  B=_B,P=_P,K=_K;
  if(P<=n){
    flag=1;
    for(inv[0]=inv[1]=1,i=2;i<P;i++)inv[i]=(P-inv[P%i])*(P/i)%P;
  }else flag=0;
  ll tmp=1LL*(m/P)*rev(m/P)%m;
  int i;
  for(w[0].set(i=1);i<=n;i++)w[i].set(i);
  for(i=1;i<=n;i++)w[i]=w[i]*w[i-1];
  for(i=1;i<=n;i++)c[i]=0;
  for(i=1;i<=n;i++)c[a[i]]++;
  Num all(1,0);
  for(i=1;i<=n;i++)all=all*w[c[i]];
  for(i=1;i<=n;i++){
    if(g[i])t.set(g[i]),f[i]=(tmp*(t*w[n-i]/all).val()+f[i])%m;
    c[a[i]]--;
    all=all/w[c[a[i]]+1]*w[c[a[i]]];
  }
}
void divide(int n){
  int i=1;
  for(int i=2;i*i<=n;i++)if(n%i==0){
    int x=1,k=0;
    while(n%i==0)n/=i,x*=i,k++;
    solve(i,x,k);
  }
  if(n>1)solve(n,n,1);
}
int main(){
  read(n),read(m);
  for(i=1;i<=n;i++)read(a[i]),b[i]=a[i];
  std::sort(b+1,b+n+1);
  for(i=1;i<=n;i++)a[i]=lower(a[i]);
  for(i=1;i<=n;i++)c[a[i]]++;
  for(i=1;i<=n;i++)add(i,c[i]);
  for(i=1;i<=n;i++)g[i]=ask(a[i]-1),add(a[i],-1);
  divide(m);
  for(ans=i=1;i<=n;i++)ans=(ans+f[i])%m;
  return printf("%d",ans),0;
}

  

时间: 2024-08-10 02:08:45

BZOJ1129 : [POI2008]Per的相关文章

BZOJ1124: [POI2008]枪战Maf

1124: [POI2008]枪战Maf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 501  Solved: 200[Submit][Status][Discuss] Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人也不同. Input 输入n人数<1000000 每个人的aim Output 你要求最

1131: [POI2008]Sta

1131: [POI2008]Sta Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 783  Solved: 235[Submit][Status] Description 给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大 Input 给出一个数字N,代表有N个点.N<=1000000 下面N-1条边. Output 输出你所找到的点,如果具有多个解,请输出编号最小的那个. Sample Input 8 1 4 5 6

BZOJ 1113: [Poi2008]海报PLA

题目 1113: [Poi2008]海报PLA Time Limit: 10 Sec  Memory Limit: 162 MB Description N个矩形,排成一排. 现在希望用尽量少的矩形海报Cover住它们. Input 第一行给出数字N,代表有N个矩形.N在[1,250000] 下面N行,每行给出矩形的长与宽.其值在[1,1000000000]2 1/2 Postering Output 最少数量的海报数. Sample Input 5 1 2 1 3 2 2 2 5 1 4 Sa

AC日记——[POI2008]BLO-Blockade 洛谷 [POI2008]BLO-Blockade

[POI2008]BLO-Blockade 思路: tarjan: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxm 500005 #define ll long long ll Count[maxn],E[maxm<<1],V[maxm<<1],head[maxn],cnt; ll size[maxm],dfn[maxn],n,m,tot,low[max

bzoj1112[POI2008]砖块Klo*

bzoj1112[POI2008]砖块Klo 题意: N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:丢掉某柱砖的一块砖.给某柱加上一块砖,现在希望用最小次数的动作完成任务.N≤100000 题解: 设一个区间长度为k,其中位数为a,比a小的元素个数为b,和为c:比a大的元素个数为d,和为e.则题目要求维护一个长度为k的滑动窗口,能求出它的b*a-c+e-d*a.故用一个维护sum,size两个值的treap来维护.然而似乎我想复杂了?比所有人代码都大1k!注意要开long

BZOJ 1124[POI2008]枪战

题面: 1124: [POI2008]枪战Maf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 659  Solved: 259[Submit][Status][Discuss] Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人也不同. Input 输入n人数<1000000 每个人的aim Output

【BZOJ 1116】 [POI2008]CLO

1116: [POI2008]CLO Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 612  Solved: 333 [Submit][Status] Description Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 你要把其中一些road变成单向边使得:每个town都有且只有一个入度 Input 第一行输入n m.1 <= n<= 100000,1 &

BZOJ 1124: [POI2008]枪战Maf

1124: [POI2008]枪战Maf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 617  Solved: 236[Submit][Status][Discuss] Description 有n个人,每个人手里有一把手枪.一开始所有人都选定一个人瞄准(有可能瞄准自己).然后他们按某个顺序开枪,且任意时刻只有一个人开枪.因此,对于不同的开枪顺序,最后死的人也不同. Input 输入n人数<1000000 每个人的aim Output 你要求最

BZOJ 1131: [POI2008]Sta

1131: [POI2008]Sta Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1370  Solved: 486[Submit][Status][Discuss] Description 给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大 Input 给出一个数字N,代表有N个点.N<=1000000 下面N-1条边. Output 输出你所找到的点,如果具有多个解,请输出编号最小的那个. Sample Input