组合计数训练题解

CF40E

题目链接

点击打开链接

题目解法

首先,如果 n , m n,m n,m 一奇一偶,那么答案为 0 0 0
原因是从行和列的角度分析, − 1 -1 1 个数的奇偶性不同

可以发现 k < max ⁡ { n , m } k<\max\{n,m\} k<max{n,m} 的性质很微妙,把它转化为有用的信息,即为:
n > m n>m n>m,则必有一行为空
可以发现,除了空行,其他所有行只需在满足行乘积为 − 1 -1 1 的情况下随便填,然后令空行满足列的限制即可
我们需要考虑空行在满足列的限制的情况下是否仍然满足行的限制,这个对 − 1 -1 1 的个数讨论一下不难证得

于是可以简单地计算,时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include <bits/stdc++.h>
using namespace std;
const int N=1100;
int n,m,k,dp[N][N],pw[N];
int cov1[N],cov2[N];
int od1[N],od2[N];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
int main(){n=read(),m=read(),k=read();if((n+m)&1){ puts("0");exit(0);}for(int i=1;i<=k;i++){int x=read(),y=read(),z=read();cov1[x]++,cov2[y]++;if(z==-1) od1[x]^=1,od2[y]^=1;}int P=read();for(int i=1;i<=n;i++) if(cov1[i]==m&&!od1[i]){ puts("0");exit(0);}for(int i=1;i<=m;i++) if(cov2[i]==n&&!od2[i]){ puts("0");exit(0);}pw[0]=1;for(int i=1;i<=max(n,m);i++) pw[i]=pw[i-1]*2%P;if(n>m){for(int i=1;i<=n;i++)if(!cov1[i]){int ans=1;for(int j=1;j<=n;j++){if(i==j) continue;if(m-cov1[j]>0) ans=1ll*ans*pw[m-cov1[j]-1]%P;}printf("%d",ans);exit(0);}}elsefor(int i=1;i<=m;i++)if(!cov2[i]){int ans=1;for(int j=1;j<=m;j++){if(i==j) continue;if(n-cov2[j]>0) ans=1ll*ans*pw[n-cov2[j]-1]%P;}printf("%d",ans);exit(0);}return 0;
}

CF140E

题目链接

点击打开链接

题目解法

先特殊提醒一下,每一层的球不是构成一个环,而是一个序列
可以发现这一层只关心选了几个颜色,而不关心选了那些颜色
所以显然有 d p i , j dp_{i,j} dpi,j 为到第 i i i 层,第 i i i 层有 j j j 个颜色的方案数
考虑转移
容斥一下,用总的方案数 - 不合法的方案数
所以 d p i , j = m j ‾ ∗ f l [ i ] , j ∑ d p i − 1 , k − j ! ∗ d p i − 1 , j dp_{i,j}=m^{\underline{j}}*f_{l[i],j}\sum{dp_{i-1,k}}-j!*dp_{i-1,j} dpi,j=mjfl[i],jdpi1,kj!dpi1,j
其中 f i , j f_{i,j} fi,j 为长度为 i i i 序列,相邻不同,颜色总数为 j j j,且每一种颜色都染过的方案数
f i , j f_{i,j} fi,j 好求,不细讲了,直接给转移式子: f i , j = f i − 1 , j − 1 + f i − 1 , j ∗ ( j − 1 ) f_{i,j}=f_{i-1,j-1}+f_{i-1,j}*(j-1) fi,j=fi1,j1+fi1,j(j1)
因为没有规定染 m m m 个颜色中的哪 j j j 个,所以要乘 m j ‾ m^{\underline{j}} mj
减去的数中因为之前已经规定好了选哪 j j j 个,所以只需要给 j j j 个数排个次序即可(不难发现, f f f 式子中并没有规定 j j j 个颜色的顺序)
时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include <bits/stdc++.h>
using namespace std;
const int MX=5100,N=1000100;
int n,m,P,l[N];
int fac[MX],perm[N];
int dp[2][MX],f[MX][MX];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
int main(){n=read(),m=read(),P=read();for(int i=1;i<=n;i++) l[i]=read();fac[0]=1;for(int i=1;i<MX;i++) fac[i]=1ll*fac[i-1]*i%P;perm[0]=1;for(int i=1;i<=m;i++) perm[i]=1ll*perm[i-1]*(m-i+1)%P;f[0][0]=1;for(int i=1;i<MX;i++) for(int j=1;j<=i;j++) f[i][j]=(f[i-1][j-1]+1ll*f[i-1][j]*(j-1))%P;int sum=0;for(int i=1;i<=l[1];i++) dp[1][i]=1ll*perm[i]*f[l[1]][i]%P,sum=(sum+dp[1][i])%P;for(int i=2;i<=n;i++){for(int j=1;j<=l[i];j++) dp[i&1][j]=0;for(int j=1;j<=l[i];j++){dp[i&1][j]=1ll*perm[j]%P*sum%P*f[l[i]][j]%P;if(j<=l[i-1]) dp[i&1][j]=(dp[i&1][j]-1ll*dp[~i&1][j]*fac[j]%P*f[l[i]][j]%P+P)%P;}sum=0;for(int j=1;j<=l[i];j++) sum=(sum+dp[i&1][j])%P;}printf("%d",sum);return 0;
}

CF482D

题目链接

点击打开链接

题目解法

显然树形 d p dp dp
考虑染的颜色只跟染的顺序的奇偶性有关
所以令 d p i , 0 / 1 dp_{i,0/1} dpi,0/1 表示在 i i i 的子树中,选了偶数还是奇数个点
考虑编号升序是好转移的,编号降序的方案和编号升序的方案一样
现在只需要计算编号升序和降序重复的方案数
不难发现,重复的条件为 ∀ p r e i − 1 ≡ s u f i + 1 \forall pre_{i-1}\equiv suf_{i+1} prei1sufi+1
可以发现这个等价于

  1. 每个子树中选的点都为偶数
  2. 每个子树中选的点都为奇数,且只有奇数个子树选到

这个可以在开一个 d p dp dp 数组 f i , 0 / 1 , 0 / 1 f_{i,0/1,0/1} fi,0/1,0/1 表示在 i i i 的子树中选出了奇数或偶数个点,所有选的子树中点数均为奇数或偶数,不难转移

时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include <bits/stdc++.h>
using namespace std;
const int N=100100,P=1e9+7;
int n,dp[N][2],f[N][2][2];
vector<int> vec[N];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
void dfs(int u){dp[u][1]=1,f[u][0][0]=f[u][0][1]=1;for(int v:vec[u]){dfs(v);int t0=dp[u][0],t1=dp[u][1];dp[u][0]=(1ll*t0*dp[v][0]+1ll*t1*dp[v][1])%P;dp[u][1]=(1ll*t0*dp[v][1]+1ll*t1*dp[v][0])%P;t0=f[u][1][1],t1=f[u][0][1];f[u][0][0]=(f[u][0][0]+1ll*f[u][0][0]*(dp[v][0]-1))%P;//子树中选了偶数个点,所有选的子树中都选了偶数个点f[u][0][1]=(f[u][0][1]+1ll*t0*dp[v][1])%P;//子树中选了偶数个点,所有选的子树中都选了奇数个点f[u][1][1]=(f[u][1][1]+1ll*t1*dp[v][1])%P;//子树中选了奇数个点,所有选的子树中都选了奇数个点}dp[u][0]=(dp[u][0]*2+1)%P,dp[u][1]=dp[u][1]*2%P;dp[u][0]=(dp[u][0]-f[u][1][1]+P)%P,dp[u][1]=(dp[u][1]-f[u][0][0]+P)%P;
}
int main(){n=read();for(int i=2;i<=n;i++) vec[read()].push_back(i);dfs(1);printf("%d",(dp[1][0]+dp[1][1]-1)%P);return 0;
}

CF325E

题目链接

点击打开链接

题目解法

先考虑 n n n 为偶数的情况
先看一个比较妙的性质:对于 i i i i + n 2 i+\frac{n}{2} i+2n,它们能到达的两个点是一样的,因为 2 i ≡ 2 i + n 2i\equiv2i+n 2i2i+n 2 i + 1 ≡ 2 i + n + 1 2i+1\equiv 2i+n+1 2i+12i+n+1
所以考虑暂时钦定 i i i 连向 2 i 2i 2i i + n 2 i+\frac{n}{2} i+2n 连向 2 i + 1 2i+1 2i+1
考虑这样整张图会形成若干个简单环,现在需要考虑合并简单环
x x x x + n 2 x+\frac{n}{2} x+2n 不在一个简单环内,那么考虑把 x x x 连到 2 x + 1 2x+1 2x+1 x + n 2 x+\frac{n}{2} x+2n 连到 2 x 2x 2x,这样可以把 2 2 2 个环合并成一个环,这个可以用并查集维护

考虑 n n n 为奇数的情况,直觉告诉我们一定是无解的
考虑来证明它:可以发现到终点 0 0 0 的点只能是 n − 1 2 \frac{n-1}{2} 2n1,而到达 n − 1 n-1 n1 的点也只能是 n − 1 2 \frac{n-1}{2} 2n1,这就矛盾了
时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
const int N=100100;
int n,fa[N],rt,nxt[N];
bool vis[N];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
void dfs(int u){vis[u]=1,fa[u]=rt;if(!vis[nxt[u]]) dfs(nxt[u]);
}
int get_father(int x){ return x==fa[x]?x:fa[x]=get_father(fa[x]);}
int main(){n=read();if(n&1){ puts("-1");exit(0);}for(int i=0;i<n/2;i++) nxt[i]=i<<1,nxt[i+n/2]=i<<1^1;for(int i=0;i<n;i++) fa[i]=i;for(int i=0;i<n;i++) if(!vis[i]) rt=i,dfs(i);for(int i=0;i<n/2;i++){int x=get_father(i),y=get_father(i+n/2);if(x!=y) fa[x]=y,nxt[i]=i<<1^1,nxt[i+n/2]=i<<1;}int pos=0;while(nxt[pos]) printf("%d ",pos),pos=nxt[pos];printf("%d 0",pos);return 0;
}

CF896D

题目链接

点击打开链接

题目解法

先考虑 n n n 个人不包含 v i p vip vip 的情况
考虑转化,把 50 50 50 元看成 1 1 1 100 100 100 元看成 − 1 -1 1
题目就是问有多少种方案,所有的前缀和都 ≥ 0 \ge 0 0
这让我们想到了卡特兰数,于是可以考虑用推卡特兰数的方法来推到 ( x , n − x ) (x,n-x) (x,nx) 处的方案数,条件是 n − x + l ≤ x ≤ n − x + r n-x+l\le x\le n-x+r nx+lxnx+r
推断方法不细讲,大概就是对 y = x + 1 y=x+1 y=x+1 作对称,最后推出来的式子应该是 ( n x ) − ( n x + 1 ) \binom{n}{x}-\binom{n}{x+1} (xn)(x+1n)

然后考虑有 v i p vip vip 的情况,即枚举个数,最终的答案即为 ∑ i = 0 n ∑ i − x + l ≤ x ≤ i − x + r ( i x ) − ( i x + 1 ) \sum\limits_{i=0}^{n}\sum\limits_{i-x+l\le x\le i-x+r}\binom{i}{x}-\binom{i}{x+1} i=0nix+lxix+r(xi)(x+1i)
可以发现对于同一个 i i i,中间的组合数都可以消掉,所以只要算最左边和最右边的组合数即可

考虑 P P P 不是质数,我们可以把 p p p 质因数分解,然后把每个计算的数拆分成 x ∗ ∏ p k α k x*\prod p_k^{\alpha_k} xpkαk,其中 ( x , P ) = 1 (x,P)=1 (x,P)=1,这个对每个 p p p 的质因子开个桶即可,考虑 P P P 的质因子不会超过 9 个,所以可以很快维护

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=100100;
int n,P,l,r;
int phi,pr[N],cnt;
int fac[N],invX[N],coef[N][35];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
int qmi(int a,int b){int res=1;for(;b;b>>=1){if(b&1) res=1ll*res*a%P;a=1ll*a*a%P;}return res;
}
void calc(){fac[0]=invX[0]=1,fac[1]=invX[1]=1;for(int i=2;i<=n;i++){int t=i;for(int j=1;j<=cnt;j++){coef[i][j]=coef[i-1][j];while(t%pr[j]==0) t/=pr[j],coef[i][j]++;}fac[i]=1ll*fac[i-1]*t%P;invX[i]=qmi(fac[i],phi-1);}
}
int binom(int x,int y){if(x<y) return 0;int res=fac[x]*invX[y]%P*invX[x-y]%P;for(int i=1;i<=cnt;i++) res=res*qmi(pr[i],coef[x][i]-coef[y][i]-coef[x-y][i])%P;return res;
}
signed main(){n=read(),P=read(),l=read(),r=read();phi=P;int t=P;for(int i=2;i*i<=t;i++)if(t%i==0){while(t%i==0) t/=i;pr[++cnt]=i,phi=phi/i*(i-1);}if(t>1) pr[++cnt]=t,phi=phi/t*(t-1);calc();int ans=0;for(int i=0;i<=n;i++){int lb=(l+i+1)/2;int rb=min((r+i)/2,i);if(lb<=rb) ans=(ans+(binom(i,lb)-binom(i,rb+1)+P)*binom(n,i)%P)%P;}printf("%lld",ans);return 0;
}

CF750G

题目链接

点击打开链接

题目解法

考虑对答案的路径分 3 种情况

  1. 一个点,方案数为 1
  2. 从祖先到子孙的路径
    令祖先的编号为 u u u,延续到下面的深度为 h h h
    则编号和为 ( 2 h + 1 − 1 ) ∗ u + ∑ i = 0 h − 1 ( 2 i − 1 ) ∗ c i (2^{h+1}-1)*u+\sum_{i=0}^{h-1} (2^i-1)*c_i (2h+11)u+i=0h1(2i1)ci,其中 c i c_i ci 为这条边是否往右走
    考虑 ∑ i = 0 h − 1 2 i ∗ c i < 2 h \sum_{i=0}^{h-1} 2^i*c_i<2^h i=0h12ici<2h,那么枚举 h h h,我们可以直接计算出对应的 u u u
    然后判断是否合法即可
  3. u u u l c a lca lca v v v 的路径( u ≠ l c a ≠ v u\neq lca\neq v u=lca=v
    令祖先的编号为 u u u,延续到下面的深度分别为 h 1 , h 2 h1,h2 h1,h2
    则编号和为 ( 2 h 1 + 1 + 2 h 2 + 1 − 2 ) ∗ u + ∑ i = 0 h 1 − 1 ( 2 i − 1 ) c i + ∑ i = 0 h 2 − 1 ( 2 i − 1 ) c i ′ (2^{h1+1}+2^{h2+1}-2)*u+\sum_{i=0}^{h1-1}(2^i-1)c_i+\sum_{i=0}^{h2-1}(2^i-1)c'_i (2h1+1+2h2+12)u+i=0h11(2i1)ci+i=0h21(2i1)ci
    同理对于相同的 h 1 , h 2 h1,h2 h1,h2,我们可以唯一确定出一个 u u u
    那么考虑计算下面有多少种路径
    考虑 2 i − 1 2^i-1 2i1 − 1 -1 1 不好计算,所以考虑枚举 c i , c i ′ c_i,c'_i ci,ci 1 1 1 的数的个数
    然后考虑 d p dp dp,令 d p i , j , 0 / 1 dp_{i,j,0/1} dpi,j,0/1 表示从低到高到了第 i i i 位,已经选了 j j j 个数,是否进位的方案数
    这样可以轻松转移,要注意 h 1 − 1 h1-1 h11 h 2 − 1 h2-1 h21 位要强制选和不选(因为必须往左和往右)

时间复杂度 O ( H 5 ) O(H^5) O(H5) H H H 为树高

#include <bits/stdc++.h>
#define int long long
using namespace std;
int s,bs[60],dp[60][120][2];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
int check(int left,int h){for(int i=h;i>=0;i--) if(left>=bs[i]-1) left-=bs[i]-1;return !left;
}
int calc(int h1,int h2,int left){int res=0;for(int i=1;i<=h1+h2;i++){int curleft=left+i;bool flg=1;for(int j=max(h1,h2)+2;j<=50;j++) if(curleft>>j&1){ flg=0;break;}if(!flg||(curleft&1)) continue;memset(dp,0,sizeof(dp));dp[0][0][0]=1;for(int j=1;j<=max(h1,h2)+1;j++)for(int k=0;k<=i;k++)for(int z=0;z<=1;z++){if(!dp[j-1][k][z]) continue;for(int x=0;x<=(j<h1);x++)for(int y=(j==h2);y<=(j<=h2);y++){int curbit=(x+y+z)%2,ne=(x+y+z)/2;if(curbit!=(curleft>>j&1ll)) continue;dp[j][k+x+y][ne]+=dp[j-1][k][z];}}res+=dp[max(h1,h2)+1][i][0];}return res;
}
signed main(){s=read();bs[0]=1;for(int i=1;i<=51;i++) bs[i]=bs[i-1]*2;int ans=1;//选n//端点u,v为祖孙关系//枚举相差层数for(int h=1;h<=50;h++){int u=s/(bs[h+1]-1);//头if(!u) continue;if(check(s-u*(bs[h+1]-1),h)) ans++;}//端点u,v不是祖孙关系for(int h1=1;h1<=50;h1++)for(int h2=1;h2<=50;h2++){int u=s/(bs[h1+1]+bs[h2+1]-3);if(!u) continue;ans+=calc(h1,h2,s-u*(bs[h1+1]+bs[h2+1]-3));}printf("%lld",ans);return 0;
}
/*
dp[i][j][0/1]:到第i位,选了j个数,是否有进位的方案数
*/

CF981H

题目链接

点击打开链接

题目解法

感觉挺好的一道题,除了多项式
显然 k k k 条路径共同经过的部分一定是一段路径
然后考虑分类讨论路径端点 u , v u,v u,v 的关系

  1. u , v u,v u,v 不是祖先关系
    答案即为 s u × s v s_u\times s_v su×sv
    其中 s u s_u su 表示在 u u u 的子树中选出 k k k 个点,并组成排列的方案数
    考虑枚举 u u u 节点的选择个数,因为只有它是可以经过不限制次的, u u u 的儿子的子树中只能选出 1 1 1 个点
    所以考虑除了 u u u i i i 个点且不考虑顺序的方案数即为 [ x i ] ∏ ( 1 + s i z v x ) [x^i]\prod (1+siz_vx) [xi](1+sizvx)
    因为是要求出这个多项式卷积的每一项的,所以考虑用分治 n t t ntt ntt 求出
    然后 s u s_u su 即为 ∑ i = 0 k ( k i ) × i ! × [ x i ] ∏ ( 1 + s i z v x ) \sum\limits_{i=0}^{k}\binom{k}{i}\times i!\times [x^i]\prod (1+siz_vx) i=0k(ik)×i!×[xi](1+sizvx)
    然后所有情况的答案即为 ( ∑ s u ) 2 (\sum s_u)^2 (su)2
    还需要减去 u u u u u u 自己的代价和 u u u u u u 的子树内的点的代价
    这用一个子树和不难求出
  2. u , v u,v u,v 是祖先关系
    这种情况稍微难办一些
    不考虑 u u u 选出 i i i 个点的方案数为 [ x i ] ∑ v ∈ s o n ( u ) ( 1 + ( n − s i z u ) x ) × ∏ w ≠ v ( 1 + s i z w x ) [x^i]\sum\limits_{v\in son(u)}{(1+(n-siz_u)x)\times \prod\limits_{w\neq v} (1+siz_wx)} [xi]vson(u)(1+(nsizu)x)×w=v(1+sizwx)
    这个也可以用分治 n t t ntt ntt 求出
    计算答案的式子和第一类情况一样

记得特判一条路径的情况
因为度数之和为 n n n,所以时间复杂度是分治 n t t ntt ntt O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include <bits/stdc++.h>
using namespace std;
const int N=300000,P=998244353;
const int G=3,Gi=332748118;
typedef pair<vector<int>,vector<int> > pvv;
typedef pair<int,int> pii;
int n,k,ans,ans2;
int rev[N],tot;
int f[N],s[N];
int siz[N],fac[N],inv[N];
vector<pii> son;
vector<int> vec[N],Gr[N];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
inline void inc(int &x,int y){x+=y;if(x>=P) x-=P;
}
int qmi(int a,int b){int res=1;for(;b;b>>=1){if(b&1) res=1ll*res*a%P;a=1ll*a*a%P; }return res;
}
vector<int> ntt(vector<int> a,bool neg){for(int i=0;i<tot;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);for(int mid=1;mid<tot;mid<<=1){int g1=qmi(neg?Gi:G,(P-1)/(mid<<1));for(int i=0;i<tot;i+=mid<<1){int gk=1;for(int j=0;j<mid;j++,gk=1ll*gk*g1%P){int x=a[i+j],y=1ll*gk*a[i+j+mid]%P;a[i+j]=x+y>=P?x+y-P:x+y,a[i+j+mid]=x-y<0?x-y+P:x-y;}}}return a;
}
vector<int> mul(vector<int> A,vector<int> B){int bit=0;while((1<<bit)<=A.size()+B.size()) bit++;tot=1<<bit;for(int i=0;i<tot;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));A.resize(tot),B.resize(tot);A=ntt(A,0),B=ntt(B,0);for(int i=0;i<tot;i++) A[i]=1ll*A[i]*B[i]%P;A=ntt(A,1);int iv=qmi(tot,P-2);for(int i=0;i<tot;i++) A[i]=1ll*A[i]*iv%P;while(!A.back()) A.pop_back();return A;
}
vector<int> add(vector<int> A,vector<int> B)if(A.size()<B.size()) swap(A,B);for(int i=0;i<B.size();i++) inc(A[i],B[i]);return A;
}
pvv pushup(pvv L,pvv R){vector<int> f=mul(L.first,R.first);vector<int> g=add(mul(L.first,R.second),mul(L.second,R.first));return make_pair(f,g);
}
pvv solve(int l,int r){if(l==r) return make_pair(vector<int>{1,son[l].first},vector<int>{son[l].second});int mid=(l+r)>>1;return pushup(solve(l,mid),solve(mid+1,r));
}
void predfs(int u,int fa){siz[u]=1;for(int v:vec[u])if(v!=fa){predfs(v,u);siz[u]+=siz[v],Gr[u].push_back(v);}
}
int C(int a,int b){ return 1ll*fac[a]*inv[b]%P*inv[a-b]%P;}
void dfs(int u){for(int v:Gr[u]) dfs(v),inc(s[u],s[v]);if(!Gr[u].size()) f[u]=s[u]=1;else{son.clear();for(int v:Gr[u]) son.push_back({siz[v],s[v]});pvv ret=solve(0,son.size()-1);vector<int> poly1=ret.first,poly2=ret.second;for(int i=0;i<min((int)poly1.size(),k+1);i++) inc(f[u],1ll*poly1[i]*C(k,i)%P*fac[i]%P);inc(s[u],f[u]);poly2=mul(vector<int>{1,n-siz[u]},poly2);for(int i=0;i<min((int)poly2.size(),k+1);i++) inc(ans2,1ll*poly2[i]*C(k,i)%P*fac[i]%P);}inc(ans,P-1ll*f[u]*(2*s[u]%P-f[u]+P)%P);
}
int main(){n=read(),k=read();if(k==1){ printf("%d\n",(1ll*n*(n-1)/2)%P);exit(0);fac[0]=1;for(int i=1;i<=n+1;i++) fac[i]=1ll*fac[i-1]*i%P;inv[n+1]=qmi(fac[n+1],P-2);for(int i=n;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%P;for(int i=1;i<n;i++){int x=read(),y=read();vec[x].push_back(y),vec[y].push_back(x);}predfs(1,-1),dfs(1);inc(ans,1ll*s[1]*s[1]%P),ans=1ll*ans*qmi(2,P-2)%P;inc(ans,ans2);printf("%d\n",ans);fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));return 0;
}

CF698F

题目链接

点击打开链接

题目解法

不是很想写,一些结论我也说不清楚也不会证,自己看题解吧
有时间可能会补,先给出代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1000100,P=1e9+7;
int n,p[N],dy1[N],dy2[N],c1[N],c2[N];
int mulp[N],fac[N],chu[N];
int pr[N],v[N],cnt;
vector<int> factor[N];
inline int read(){int FF=0,RR=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;return FF*RR;
}
int main(){n=read();for(int i=1;i<=n;i++) mulp[i]=1;c1[1]++;factor[1].push_back(1),chu[1]=1;for(int i=2;i<=n;i++){if(!v[i]){pr[++cnt]=i,chu[i]=n/i,c1[n/i]++;for(int j=i;j<=n;j+=i) factor[j].push_back(i),mulp[j]*=i;}for(int j=1;j<=cnt&&pr[j]<=n/i;j++){v[pr[j]*i]=pr[j];if(v[i]==pr[j]) break;}}for(int i=1;i<=n;i++) c2[mulp[i]]++;for(int i=1;i<=n;i++) p[i]=read();for(int i=1;i<=n;i++){if(!p[i]) continue;if(factor[i].size()!=factor[p[i]].size()){ puts("0");exit(0);}for(int j=0;j<factor[i].size();j++) if(chu[factor[i][j]]!=chu[factor[p[i]][j]]){ puts("0");exit(0);}int mxfac1=factor[i].back(),mxfac2=factor[p[i]].back();if(dy1[mxfac1]&&dy1[mxfac1]!=mxfac2){ puts("0");exit(0);}if(dy2[mxfac2]&&dy2[mxfac2]!=mxfac1){ puts("0");exit(0);}if(!dy1[mxfac1]&&!dy2[mxfac2]) c1[chu[mxfac1]]--;dy1[mxfac1]=mxfac2,dy2[mxfac2]=mxfac1;c2[mulp[p[i]]]--;}fac[0]=1;for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%P;int ans=1;for(int i=1;i<=n;i++) ans=1ll*ans*fac[c1[i]]%P*fac[c2[i]]%P;printf("%d",ans);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/140726.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

索引(含B树、B+树)

1、索引&#xff08;index&#xff09; 索引是在数据库表的字段上添加的&#xff0c;是为了提高查询效率存在的一种机制。 一张表的一个字段可以添加一个索引&#xff0c;当然&#xff0c;多个字段联合起来也可以添加索引。 索引相当于一本书的目录&#xff0c;是为了缩小扫描…

Avl树(有详细图解)

目录 介绍 引入 概念 特点 模拟实现 思路 插入 旋转 左旋 无子树 有子树 右旋 无子树 有子树 左右旋 引入(也就是有子树版本的抽象图解) 解决方法(也就是左右旋) 总结 无子树(也就是curright的位置就是newnode) 有子树 模型高度解释 旋转 更新三个…

深度学习修炼(二)全连接神经网络 | Softmax,交叉熵损失函数 优化AdaGrad,RMSProp等 对抗过拟合 全攻略

文章目录 1 多层感知机&#xff08;全连接神经网络&#xff09;1.1 表示1.2 基本概念1.3 必要组成—激活函数1.4 网络结构设计 2 损失函数2.1 SOFTMAX操作2.2 交叉熵损失函数 3 优化3.1 求导计算过于复杂&#xff1f;3.2 链式法则导致的问题&#xff1f;3.3 梯度下降算法的改进…

八大排序(二)快速排序

一、快速排序的思想 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其基本思想为&#xff1a;任取待排序元素序列中的某元素作为基准值&#xff0c;按照该排序码将待排序集合分割成两子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右…

免费的AI写作软件-智能AI写作工具

我们要谈的话题是AI写作&#xff0c;尤其是免费AI写作&#xff0c;以及147SEOAI写作免费工具。您是否曾经为了创作文章而感到煞费苦心&#xff1f;是否一直在寻找一种能够轻松生成高质量文章的方法&#xff1f; 147GPT批量文章生成工具​www.147seo.com/post/2801.html​编辑ht…

Flink TaskManger 内存计算实战

Flink TaskManager内存计算图 计算实例 案例一、假设Task Process内存4GB。 taskmanager.memory.process.size4096m 先排减JVM内存。 JVM Metaspace 固定内存 256mJVM Overhead 固定比例 process * 0.1 4096 * 0.1 410m 得到 Total Flink Memory 4096-256-410 3430m 计…

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows 大家好我是艾西&#xff0c;最近研究了下 l4d2&#xff08;求生之路2&#xff09;这款游戏的搭建以及架设过程。今天就给喜欢l4d2这款游戏的小伙伴们分享下怎么搭建架设一个自己的服务器。毕竟自己当服主是热爱游…

华为云云耀云服务器L实例评测|redis漏洞回顾 MySQL数据安全解决 搭建主从集群MySQL 相关设置

前言 最近华为云云耀云服务器L实例上新&#xff0c;也搞了一台来玩&#xff0c;期间遇到过MySQL数据库被攻击的情况&#xff0c;数据丢失&#xff0c;还好我有几份备份&#xff0c;没有造成太大的损失&#xff1b;后来有发现Redis数据库被攻击的情况&#xff0c;加入了redis密…

基于springboot+vue的校园外卖服务系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

【Vue+Element-UI】实现登陆注册界面及axios之get、post请求登录功能实现、跨域问题的解决

目录 一、实现登陆注册界面 1、前期准备 2、登录静态页实现 2.1、创建Vue组件 2.2、静态页面实现 2.3、配置路由 2.4、更改App.vue样式 2.5、效果 3、注册静态页实现 3.1、静态页面实现 3.2、配置路由 3.3、效果 二、axios 1、前期准备 1.1、准备项目 1.2、安装…

原生HTML实现marquee向上滚动效果

实现原理&#xff1a;借助CSS3中animation动画以及原生JS克隆API <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /…

Northstar 量化平台

基于 B/S 架构、可替代付费商业软件的一站式量化交易平台。具备历史回放、策略研发、模拟交易、实盘交易等功能。兼顾全自动与半自动的使用场景。 已对接国内期货股票、外盘美股港股。 面向程序员的量化交易软件&#xff0c;用于期货、股票、外汇、炒币等多种交易场景&#xff…

OpenCV中的HoughLines函数和HoughLinesP函数到底有什么区别?

一、简述 基于OpenCV进行直线检测可以使用HoughLines和HoughLinesP函数完成的。这两个函数之间的唯一区别在于,第一个函数使用标准霍夫变换,第二个函数使用概率霍夫变换(因此名称为 P)。概率版本之所以如此,是因为它仅分析点的子集并估计这些点都属于同一条线的概率。此实…

php文件上传功能(文件上传)

实现文件上传是Web开发中常用的功能之一&#xff0c;而PHP也是支持文件上传的。那么&#xff0c;下面我们就来介绍一下常用的PHP实现文件上传的方法。 使用HTML表单实现文件上传 HTML表单是Web开发中最基本的元素之一&#xff0c;它可以接收用户输入的数据&#xff0c;并通过…

论文阅读_大语言模型_Llama2

英文名称: Llama 2: Open Foundation and Fine-Tuned Chat Models 中文名称: Llama 2&#xff1a;开源的基础模型和微调的聊天模型 文章: http://arxiv.org/abs/2307.09288 代码: https://github.com/facebookresearch/llama 作者: Hugo Touvron 日期: 2023-07-19 引用次数: 11…

C语言数组和指针笔试题(四)(一定要看)

目录 二维数组例题一例题二例题三例题四例题五例题六例题七例题八例题九例题十例题十一 结果 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412;个人主页 &#x1f978;&#x1f978;&#x1f978;C语言 &#x1f43f;️…

SWC 流程

一个arxml 存储SWC &#xff08;可以存多个&#xff0c;也可以一个arxml存一个SWC&#xff09;一个arxml 存储 composition &#xff08;只能存一个&#xff09;一个arxml 存储 system description (通过import dbc自动生成system) 存储SWC和composition的arxml文件分开&#…

如何用好免费的ChatGPT

如何用好免费的ChatGPT 前言ChatGPT使用入口在线体验地址&#xff1a;点我体验 ChatGPT介绍ChatGPT初级使用技巧初级使用技巧&#xff1a;清晰明了的问题表达 ChatGPT中级使用语法中级使用语法&#xff1a;具体化问题并提供背景信息 ChatGPT高级使用高级使用&#xff1a;追问、…

安防监控系统/视频云存储/视频监控平台EasyCVR无法级联上级平台,该如何解决?

安防视频监控系统EasyCVR平台能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#xff0c;也…