标签:校内 部落 int Mo ret return ll 模拟 fo
Description
T次询问,每次询问给出n,m,k,P,问n个点的所有有标号生成森林中,连通块数为m的方案中,从每棵树中选择一个度数<=k的点的方案数对P取模的方案数
n<=10^9,m,k<=100
Solution
我们先选关键点,再数生成树
显然任意m个点的方案数是相等的,我们只需要指定m个关键点,答案乘上(mn)
考虑枚举这m个点的度数,设总度数为D,剩下的部分为,n-m个点,D棵有标号有根树的生成森林个数
考虑如何做n个点m棵有标号有根树,因为是有根树,新建一个点n+1向所有根连边,这个等价于n+1个点,点n+1的度数为m的树的个数
用prufer序计数,相当于选择m-1个位置填n+1,剩下的位置填1~n,方案数为(m−1n−1)nn−m
接下来问题相当于,将n个物品分为m组,每组数量<=k
设F[n][m]表示答案,考虑第n个物品在哪一组,如果加入后不合法相当于这一组本来就有k个,枚举是哪k个剩余的相当于是F[n-1-k][m-1]
所以我们有F[n][m]=F[n−1][m]∗m−F[n−1−k][m−1]∗(kn−1)∗m
最后将两部分合并,有一个(m−1n−1)由于P不一定为质数所以直接暴力分解质因数
单次询问复杂度为O(m2k+mk2+mklog2P)
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e4+5;
int ty,n,m,k,Mo,lim,cnt[N],p[N];
ll f[105][N],C[N][105];
ll pwr(ll x,int y) {
ll z=1;
for(;y;y>>=1,x=x*x%Mo)
if (y&1) z=z*x%Mo;
return z;
}
ll calc() {
ll ret=1;
fo(i,1,p[0]) ret=ret*pwr(p[i],cnt[i])%Mo;
return ret;
}
ll ins(int x,int f) {
if (!x) return 1;
fo(i,1,p[0]) while (!(x%p[i])) x/=p[i],cnt[i]+=f;
return x;
}
void exgcd(ll a,ll b,ll &x,ll &y) {
if (!b) {x=1;y=0;return;}
ll xx,yy;
exgcd(b,a%b,xx,yy);
x=yy;y=xx-a/b*yy;
}
ll inv(ll x) {
if (x==1) return 1;
ll a,b;
exgcd(x,Mo,a,b);
return (a%Mo+Mo)%Mo;
}
ll Comb(int n,int m) {
if (m>n-m) m=n-m;
fo(i,1,p[0]) cnt[i]=0;ll ret=1;
fo(i,n-m+1,n) ret=ret*ins(i,1)%Mo;
fo(i,1,m) ret=ret*inv(ins(i,-1))%Mo;
ret=ret*calc()%Mo;
return ret;
}
void inc(ll &x,ll y) {x=x+y>=Mo?x+y-Mo:x+y;}
void dec(ll &x,ll y) {x=x-y<0?x-y+Mo:x-y;}
int main() {
freopen("islands.in","r",stdin);
freopen("islands.out","w",stdout);
for(scanf("%d",&ty);ty;ty--) {
scanf("%d%d%d%d",&n,&m,&k,&Mo);
if (Mo==1) {puts("0");continue;}
if (n==m) {puts("1");continue;}
p[0]=0;int x=Mo;
for(int i=2;i*i<=x;i++) {
if (!(x%i)) p[++p[0]]=i;
while (!(x%i)) x/=i;
}
if (x>1) p[++p[0]]=x;
fo(i,0,m*k) {
C[i][0]=1;
fo(j,1,min(i,k)) C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mo;
}
fo(i,0,m) fo(j,0,i*k) f[i][j]=0;f[0][0]=1;
fo(i,1,m) {
f[i][0]=1;
fo(j,1,i*k) {
inc(f[i][j],f[i][j-1]*i%Mo);
if (j>=k+1) dec(f[i][j],f[i-1][j-k-1]*C[j-1][k]%Mo*i%Mo);
}
}
fo(i,1,p[0]) cnt[i]=0;
ll ans=0,C=1;
fo(i,1,m*k) {
if (i>n-m) break;
(ans+=f[m][i]%Mo*pwr(n-m,n-m-i)%Mo*calc()%Mo*C%Mo)%=Mo;
C=C*ins(n-m-i,1)%Mo;
C=C*inv(ins(i,-1))%Mo;
}
printf("%lld\n",ans*Comb(n,m)%Mo);
}
return 0;
}
标签:校内,部落,int,Mo,ret,return,ll,模拟,fo 来源: https://blog.csdn.net/alan_cty/article/details/94398580
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。