• 【题解】Killer Names($O(nlog n)$做法)


    【题解】Killer Names((O(nlog n))做法)

    HDU - 6143

    感觉好久没做过这种直来直去的组合题,过来水一篇题解。还以为要写一个(MTT)或者三模数(NTT),想了想HDU这种老年机子还是算了,最后发现(O(n^2))就行了

    题意翻译过后就是要求一个式子:

    [sum_{i+jle m}{mchoose i}{m-ichoose j}{nrace i}{nrace j}i!j! ]

    钦定两边分别出现(i,j({mchoose i}{m-ichoose j}))种不同的颜色,然后再随意分布(({nrace i}{nrace j}i!j!))

    感觉这很可以卷积一下,但是我们这里是小于号咋办

    假设大家都会母函数和NTT和卷积,

    [g_i=sum_{i+j= m}{mchoose i}{m-ichoose j}{nrace i}{nrace j}i!j! \ G(x)=sum_{i=0}^mg_ix^i ]

    那么这个式子可以化成

    [G(x){(1+x+x^2+x^3dots)}=G(x){1over 1-x} ]

    就构造了一个小于号出来。好我们来拆这个式子

    拆出来后是这样的

    [sum_{i+jle m}{m!over (m-i-j)!}{nrace i}{nrace j} ]

    你以为不能拆?但是!!!!!!别忘了(g_i)是等于号啊,所以(i+j=m),所以((m-i-j)!=1),因此继续化简,这样我们就可以卷积了。

    [dfrac {g_i} {m!}=sum_{i+j=m}{nchoose i}{nchoose j} ]

    我们不需要多项式球逆,只需要(O(n))构造(1over 1-x)就行,NTT求一行斯特林数也是(O(nlog n))所以复杂度(O(nlog n))

    而且还可以加强一下,比如两边名字长度不同之类的...必须用至少(k)个不同的字母之类的...

    但是这里模数居然不是清真的(998244353),所以你得写三模数NTT再CRT合并!!那对不起我赶时间还是写(O(n^2))算这个式子...

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;  typedef long long ll;
    inline int qr(){
          register int ret=0,f=0;
          register char c=getchar();
          while(c<48||c>57)f|=c==45,c=getchar();
          while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
          return f?-ret:ret;
    }
    const int maxn=2e3+1;
    const int mod=1e9+7;
    int s[maxn][maxn];
    int jc[maxn],inv[maxn];
    
    inline int ksm(const int&ba,const int&p){
          register int ret=1;
          for(register int t=p,b=ba%mod;t;t>>=1,b=1ll*b*b%mod) if(t&1) ret=1ll*ret*b%mod;
          return ret;
    }
    
    inline void pre(const int&n){
          s[0][0]=1;
          for(register int t=1;t<=n;++t)
    	    for(register int i=1;i<=n;++i)
    		  s[t][i]=(1ll*s[t-1][i]*i%mod+s[t-1][i-1])%mod;
          inv[0]=jc[0]=1;
          for(register int t=1;t<=n;++t) jc[t]=1ll*jc[t-1]*t%mod;
          inv[n]=ksm(jc[n],mod-2);
          for(register int t=n-1;t;--t) inv[t]=1ll*inv[t+1]*(t+1)%mod;
    }
    
    inline int c(const int&n,const int&m){
          if(n<m) return 0;
          return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
    }
    
    int n,m;
    int main(){
          pre(2e3);
          int T=qr();
          while(T--){
    	    n=qr(); m=qr(); int ans=0,ed=min(n,m);  int*S=s[n];
    	    for(register int t=1;t<=ed;++t)
    		  for(register int i=1;t+i<=m&&i<=n;++i)
    			ans=(ans+1ll*c(m,t)*c(m-t,i)%mod*S[t]%mod*S[i]%mod*jc[t]%mod*jc[i]%mod)%mod;
    	    printf("%d
    ",ans);
          }
          return 0;
    }
    
    
    
  • 相关阅读:
    python学习之函数基础
    Java并发编程之支持并发的list集合你知道吗
    Java并发编程之CAS第三篇-CAS的缺点及解决办法
    Java并发编程之CAS二源码追根溯源
    Java并发编程之CAS第一篇-什么是CAS
    Java并发编程之验证volatile指令重排-理论篇
    【免费百度网盘不限速】爱奇艺万能联播 百度网盘不限速的方法
    Java并发编程之验证volatile不能保证原子性
    Java并发编程之验证volatile的可见性
    Java并发编程学习前期知识下篇
  • 原文地址:https://www.cnblogs.com/winlere/p/11406885.html
Copyright © 2020-2023  润新知