Codeforces 1649E Tyler and Strings 数论,数据结构

4 篇文章 0 订阅
2 篇文章 0 订阅

文章目录


题目链接
一道非常经典的数据结构辅助计数题.

题意

给定字符串 s s s t t t,将 s s s中的字母随意排列,求排列的字典序小于 t t t的方法由多少种.

题解

本题的字母有 2 × 1 0 5 2\times 10^5 2×105种,因此需要用到数据结构辅助.我们不妨来思考一下只有 26 26 26种字母时候的做法.
首先统计 s s s中每种字母的个数,然后开始对 t t t的第一位进行计算.可以发现, s s s的第一位放置的情况只有 3 3 3种.其中 s 1 > t 1 s_1>t_1 s1>t1的情况不合法直接忽略,而 s 1 = t 1 s_1=t_1 s1=t1的情况需要进行后续计算, s 1 < t 1 s_1<t_1 s1<t1的情况,后面的所有字母都可以随意排列.因此合法的数量就是去掉这一位之后的排列总数.那么怎么计算呢?
可以知道, s s s的全部排列总数为
n ! ∏ i = 1 k c n t k ! \frac{n!}{\prod_{i=1}^{k}cnt_k!} i=1kcntk!n!
所以这一位确定小的排列方式总数为 ( n − 1 ) ! ∏ i = 1 k c n t k ! c n t [ s 1 < t 1 ] \frac{(n-1)!}{\prod_{i=1}^{k}cnt_k!}cnt[s_1<t_1] i=1kcntk!(n1)!cnt[s1<t1],其中左边带分数线那一部分称为排列基数 s s s.
重新调整下一位确定小的排列基数 s s s,做法就是先乘 n − 1 n-1 n1的逆元和 c n t [ t 1 ] cnt[t_1] cnt[t1],然后我们在 c n t cnt cnt数组中减掉 t 1 t_1 t1,如果减完之后 c n t cnt cnt数组的这一位小于 0 0 0,那说明接下来所有的排法都是确定小或者确定大的,那就不用算了,直接把循环 b r e a k break break掉就可以了.
那么最后的问题就是现在有这么多种字母,怎么对每一位求出 s i < t i s_i<t_i si<ti的选法数量.
明显用树状数组就可以了,因此本题就迎刃而解了,谢谢大家.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rel register ll
#define rec register char
#define gc getchar
// #define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?-1:*p1++)
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
char buf[1<<23],*p1=buf,*p2=buf;
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) pc('-'),x=-x;
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
namespace ran { 
  const unsigned k1=53,k2=109,mak=4294967295,mod=1e9+7;
  typedef unsigned long long ull;
  ull sa,sb,ct;
  ull rnd() {
    sa=(sa*k1+k2)%mod;
    sb=(sb*k2+k1)%mod;
    (ct*(ct+1)>>1&1)?sa^=sb^=sa^=sb:0,++ct>mod?ct-=mod:0;
    return sa;
  }
  ull rndll() {
    sa=(sa*k1+k2)&mak;
    sb=(sb*k2+k1)&mak;
    (ct*(ct+1)>>1&1)?sa^=sb^=sa^=sb:0,++ct>mod?ct-=mod:0;
    return sa<<32|sb;
  } 
  ull rnd(ull l,ull r) {
    return rnd()%(r-l+1)+l;
  }
  ull rndll(ull l,ull r) {
    return rndll()%(r-l+1)+l;
  }
}
const int yuzu=2e5,mod=998244353;
typedef ll fuko[yuzu|10];
fuko jic={1},inv,a,b,c;
ll kasumi(ll a,ll b=mod-2) {
  ll zw=1;
  for (;b;b>>=1,a=a*a%mod) if (b&1) zw=zw*a%mod;
  return zw;
}
struct _bit {
  fuko g;
  void add(int x,int v) {
    for (;x<=yuzu;x+=x&-x) g[x]+=v;
  }
  ll query(int x) {
    ll zw=0;
    for (;x;x&=x-1) zw+=g[x];
    return zw;
  }
}zw;
int main() {
  int i,n,m;
  read(n),read(m);
  for (i=1;i<=n;++i) jic[i]=jic[i-1]*i%mod;
  inv[n]=kasumi(jic[n]);
  for (i=n-1;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
  for (i=1;i<=n;++i) a[i]=read(),++c[a[i]],zw.add(a[i],1);
  for (i=1;i<=m;++i) b[i]=read();
  ll s=jic[n-1],ans=0,t=n-1;
  for (i=1;i<=yuzu;++i) c[i]?s=s*inv[c[i]]%mod:0;
  for (i=1;i<=m;++i) {
    ans=(ans+zw.query(b[i]-1)*s)%mod;
    s=s*c[b[i]]%mod*kasumi(t)%mod;
    --t,--c[b[i]],zw.add(b[i],-1);
    if (c[b[i]]<0) break;
    if (i==n&&i<m) {
      ans=(ans+1)%mod;
      break;
    }
  }
  printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值