第一类斯特林数的介绍(n^2)复杂度
例题链接[HDU 3625]Examining the Rooms
圆排列:把n个元素排在一个圆周上,如果旋转之后两个圆周上的排列一样,那么这两个排列相同 n个元素的圆排列=n-1个元素的排列数
第一类斯特林数S(n,m)表示把n个不同元素构成m个圆排列的方案数
由定义得S(n,0)=0,S(n,n)=1
第一类斯特林数有递推公式S(n,m)=S(n−1,m−1)+(n−1)×S(n−1,m)
证明:我们考虑把第n个元素放在什么位置。如果单独形成一个圆排列,则答案就是S(n-1,m-1).
如果插入到原来的m个圆排列中,那本质上就是在n-1个元素中选一个插到它后面去(定义“后面”为顺时针方向下一个数,定义为逆时针同理),有n-1种选法,答案就是(n-1)S(n-1,m)
n个房间都有钥匙,1号门不能破门而入,要打开其他门有一种环状关系 比方说第i号房间有第j号房间的钥匙,那么你就要去找第i号房间的钥匙的所在地或者破门而入 - - - - 总之理解一下这个连环关系
ll dp[50][50];
int main(){
ll t=read();
while(t--){
ll n=read();
ll k=read();
ll ans=1;
for(int i=0;i<=n;i++){
dp[i][0]=0;
dp[i][i]=1;
for(int j=1;j<=i;j++){
dp[i][j]=dp[i-1][j-1]+(i-1)*dp[i-1][j];
}
}
for(int i=1;i<=n;i++){
ans=ans*i;
}
ll add=0;
for(int i=1;i<=k;i++){
add=add+dp[n][i]-dp[n-1][i-1];
}
double rnm=add*1.0/ans;
printf("%.4f\n",rnm);
}
return 0;
}
例题链接HDU 4372 Count the Buildings
n个房子 高度1–n 要你来重新排列这些房子 使得能够在某个房子处,从左看去有f个房子 从右看去有b个房子 的方案数
显然当我们在高度为n的房子时,左/右览都可以看完,那么以n为衔接点 分为两组 左边一组(x) 右边一组(y) 且满足x+y=n-1 (n定下来了,不考虑)
那么显然在n的两侧,只要两侧最值与n相邻,其余楼层随便排列即可 于是相当于要计算 第一类Stirling数 (n-1,x-1+y-1) *C(x-1+y-1,y-1); C是组合数计算式
ll dp[2001][2001];
ll c[2001][2001];
inline void pre(ll n){
for(ll i=1; i<=n; i++) c[i][0]=1;
dp[1][1]=c[1][1]=1;
for(ll i=2; i<=n; i++){
for(ll j=1; j<=i; j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mods;
dp[i][j]=(dp[i-1][j-1]+(i-1)*dp[i-1][j])%mods;
}
}
}
int main(){
ll t=read();
ll p=2000;
pre(p);
while(t--){
ll n=read();
ll f=read();
ll b=read();
if(f+b-2<=2000){
printf("%lld\n",dp[n-1][f+b-2]*c[f+b-2][b-1]%mods);
}
else{
printf("0\n");
}
}
return 0;
}
第二类 Stirling数 简介
例题 HDU 2643
n个同学的排名情况总数 取模 排名可以并列
显然是一个n个不同的求放入n个可区分的盒子而且盒子可以空
那么直接套用第二类 Stirling数计算式
ll dp[2001][2001];
ll c[2001];
inline void pre(ll n){
for(int i=1; i<=n; i++){
dp[i][i]=dp[i][1]=1;
for(int j=1;j<i;j++){
dp[i][j]=(j*dp[i-1][j]%mods+dp[i-1][j-1])%mods;
}
}
}
int main(){
ll t=read();
ll op=150;
pre(op);
c[1]=1;
c[0]=1;
for(int i=1;i<=op;i++){
c[i]=(c[i-1]*i)%mods;
}
while(t--){
ll n=read();
ll add=0;
for(int i=1;i<=n;i++){
add=(add+dp[n][i]*c[i]%mods)%mods;
}
printf("%lld\n",add);
}
return 0;
}
卡特兰数 (应用待更)
如何计算组合数 C(n,m,mod) 其中n,m,mod 范围很大 1e9
在此直接上exlucas 扩展定理
例题 HDU 3439
n个元素 1–n 全排列有n!种 ,我们要找恰好k个元素 即元素值与下标相等的方案数 对mod 取模
整体来看就是求C(n,k,mod)*H(n-k,mod)
H是错排函数
#include <cstdio>
#include <iostream>
#include <vector>
#define ll long long
#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
inline ll read(){char c=getchar();ll f=1,x=0;while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^'0');c=getchar();}return x*f;}
ll gcd(ll a,ll b){
if(a<0)
a=-a;
if(b<0)
b=-b;
return b==0?a:gcd(b,a%b);
}
ll qpn(ll a,ll b, ll p){ll ans = 1;a%=p;while(b){if(b&1){ans = (ans*a)%p;--b;}a =(a*a)%p;b >>= 1;}return ans%p;}//逆元 (分子*qp(分母,mod-2,mod))%mod;
ll fact_pow(ll n,ll p)
{
ll res=0;
while(n)
{
n/=p;
res+=n;
}
return res;
}
ll mult(ll a,ll b,ll p)
{
a%=p;
b%=p;
ll r=0,v=a;
while(b)
{
if(b&1)
{
r+=v;
if(r>p)
r-=p;
}
v<<=1;
if(v>p)
v-=p;
b>>=1;
}
return r;
}
ll quick_pow(ll a,ll b,ll p)
{
ll r=1,v=a%p;
while(b)
{
if(b&1)
r=mult(r,v,p);
v=mult(v,v,p);
b>>=1;
}
return r;
}
ll pow_mod(ll x,ll n,ll mod)
{
ll res=1;
while(n)
{
if(n&1)
res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
ll t=x;
x=y,y=t-(a/b)*y;
}
ll INV(ll a,ll b)
{
ll x,y;
return exgcd(a,b,x,y),(x%b+b)%b;
}
ll crt(ll x,ll p,ll mod)
{
return INV(p/mod,mod)*(p/mod)*x;
}
ll FAC(ll x,ll a,ll b)
{
if(!x)
return 1;
ll ans=1;
for(ll i=1; i<=b; i++)
if(i%a)
ans*=i,ans%=b;
ans=pow_mod(ans,x/b,b);
for(ll i=1; i<=x%b; i++)
if(i%a)
ans*=i,ans%=b;
return ans*FAC(x/a,a,b)%b;
}
ll C(ll n,ll m,ll a,ll b)
{
ll N=FAC(n,a,b),M=FAC(m,a,b),Z=FAC(n-m,a,b),sum=0,i;
for(i=n; i; i=i/a)
sum+=i/a;
for(i=m; i; i=i/a)
sum-=i/a;
for(i=n-m; i; i=i/a)
sum-=i/a;
return N*pow_mod(a,sum,b)%b*INV(M,b)%b*INV(Z,b)%b;
}
ll exlucas(ll n,ll m,ll p)
{
ll t=p,ans=0,i;
for(i=2; i*i<=p; i++)
{
ll k=1;
while(t%i==0)
{
k*=i,t/=i;
}
ans+=crt(C(n,m,i,k),p,k),ans%=p;
}
if(t>1)
ans+=crt(C(n,m,t,t),p,t),ans%=p;
return ans % p;
}
ll H(ll x,ll p)
{
ll ans=0;
if(x==0)return 1;
x=x%(2*p);
if(x==0)x=2*p;
for(int i=2;i<=x;++i)
ans=(ans*i+(i%2==0?1:-1))%p;
return (ans+p)%p;
}
/*最小公倍数lcm(a,b)=a*b/gcd(a,b);
gcd(ka,kb)=k*gcd(a,b);
gcd(s/a,s/b)=s/gcd(a,b);
gcd(a,b)=gcd(a,b-a);
gcd(x^a-1,x^b-1)=x^gcd(a,b)-1;
gcd(f[a],f[b])=f[gcd(a,b)];
lcm(ka,kb)=k*lcm(a,b);
lcm(f[a],f[b])=f[lcm(a.b)];*/
int main(){
ll t=read();
ll add=0;
while(t--){
add++;
ll n=read();
ll k=read();
ll m=read();
printf("Case %lld: %lld\n",add,exlucas(n,k,m)*H(n-k,m)%m);
}
return 0;
}
欧拉函数 phi(n) 表示小于n的数中 与n互质的数的个数
例题 LightOJ1370
对于a[]数组 给你n个数,定义F(x)=Σ num num∈phi(x) ;
要我们求出 每个phi(x)>=a[i] 时的F(x)值
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define Mod 1000000007
#define eps 1e-6
#define ll long long
#define INF 0x3f3f3f3f
#define MEM(x,y) memset(x,y,sizeof(x))
#define Maxn 1100000
using namespace std;
int T,n;
int phi[Maxn+1];//存欧拉函数
bool isPrime[Maxn+1];//存素数
int a[Maxn];
void Eular()//求欧拉函数
{
for(int i=1;i<=Maxn;i++) phi[i]=i;
memset(isPrime,true,sizeof(isPrime));
isPrime[0]=isPrime[1]=false;
phi[1]=0;
for(int i=2;i<=Maxn;i++)
{
if(isPrime[i])
{
for(int j=i;j<=Maxn;j+=i)
{
isPrime[j]=false;
phi[j] -= phi[j]/i;
}
}
}
}
int main()
{
int Case=0;
Eular();//打表
cin>>T;
while(T--)
{
MEM(a,0);
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
ll sum=0;
sort(a,a+n);
int pos=1;
for(int i=0;i<n;i++)
{
for(int j=pos;j<Maxn;j++)
{
if(phi[j]>=a[i])
{
sum+=j;
pos=j;
break;
}
}
}
printf("Case %d: %lld Xukha\n",++Case,sum);
}
}
互质公式推导以及线性筛 例题CF D题 难度分2000
一个数num 他的质因子记为p1 —px 我们如果能从质因子中选出pi与pj
使得 gcd(pi+pj,num)=1 则输出pi pj 否则输出 -1
设 p1 , p2 , p3 , … , pm 为 ai 的质因子 , d1 = p1^k , d2 = ai / p1^k
其中 p1 为 ai 的最小质因子 , ai % p^k = 0 且 ai % p1^(k + 1) != 0
那么显然 (d1+d2)%p1≠0,(d1+d2)%p2≠0,…,(d1+d2)%pm≠0
所以 ai 的所有质因子 d1 + d2 都不包含 , 即 d1 + d2 与 ai 互质 ( 当 d2 = 1 时答案为 -1 )
而本题数据范围很大 , 所以我们得先用线性筛找出 1 ~ 1e7 内每个数的 p1 然后再操作
#include<bits/stdc++.h>
using namespace std;
int prime[10000100],minprime[10000100];
int euler(int n)
{
int c = 0;
for(int i = 2 ; i <= n ; i ++)
{
if(!minprime[i]) prime[++ c] = i , minprime[i] = i;
for(int j = 1 ; j <= c && i * prime[j] <= n ; j++)
{
minprime[i * prime[j]] = prime[j];
if(i % prime[j] == 0) break;
}
}
return c;
}
const int N = 5e5 + 10;
int n , a[N] , ans[N][2];
signed main()
{
ios::sync_with_stdio(false);
euler(1e7);
cin >> n;
for(int i = 1 ; i <= n ; i ++) cin >> a[i];
for(int i = 1 ; i <= n ; i ++)
{
int x = a[i] , now = 1 , mi = minprime[x];
while(x % mi == 0) x /= mi , now *= mi;
if(now != 1 && x != 1) ans[i][0] = now , ans[i][1] = x;
else ans[i][0] = -1 , ans[i][1] = -1;
}
for(int j = 0 ; j <= 1 ; j ++)
{
for(int i = 1 ; i <= n ; i ++) cout << ans[i][j] << " ";
cout << '\n';
}
return 0;
}
今日止步 二分图算法 再接再厉吧 ,晚上学习二分图算法
未AC的题 分解质因子+HK算法
这个题卡了匈牙利算法 QAQ
每天都要努力啊 !