B组T4 JZOJ 1353 有趣的数列
题目
我们称一个长度为
2
n
2n
2n的数列是有趣的,当且仅当该数列满足以下三个条件:
(1)它是从
1
1
1到
2
n
2n
2n共
2
n
2n
2n个整数的一个排列
{
a
i
}
\{a_i\}
{ai};
(2)所有的奇数项满足
a
1
<
a
3
<
…
<
a
2
n
−
1
a_1<a_3<…<a_{2n-1}
a1<a3<…<a2n−1,所有的偶数项满足
a
2
<
a
4
<
…
<
a
2
n
a_2<a_4<…<a_{2n}
a2<a4<…<a2n;
(3)任意相邻的两项
a
2
i
−
1
与
a
2
i
(
1
≤
i
≤
n
)
a_{2i-1}与a_{2i}(1≤i≤n)
a2i−1与a2i(1≤i≤n)满足奇数项小于偶数项,即:
a
2
i
−
1
<
a
2
i
a_{2i-1}<a_{2i}
a2i−1<a2i。
现在的任务是:对于给定的
n
n
n,请求出有多少个不同的长度为
2
n
2n
2n的有趣的数列。因为最后的答案可能很大,所以只要求输出答案
m
o
d
P
\bmod P
modP的值。
分析
这道题目也就是在求 C 2 n n n + 1 \frac{C_{2n}^n}{n+1} n+1C2nn,由于可能不存在逆元,所以分解质因数求答案
代码
#include <cstdio>
#define rr register
using namespace std;
const int N=2e6+5;
int c[N],v[N],prime[N],lim,n,mod,ans=1,cnt;
inline void dfs(int x,int z){
if (x==1) return;
c[v[x]]+=z,dfs(x/v[x],z);
}
inline signed ksm(int x,int y){
rr int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
signed main(){
scanf("%d%d",&n,&mod),lim=2*n;
for (rr int i=2;i<=lim;++i){
if (!v[i]) v[i]=prime[++cnt]=i;
for (rr int j=1;j<=cnt&&prime[j]*i<=lim;++j){
v[i*prime[j]]=prime[j];
if (i%prime[j]==0) break;
}
}
for (rr int i=n+2;i<=2*n;++i) dfs(i,1);
for (rr int i=2;i<=n;++i) dfs(i,-1);
for (rr int i=2;i<=2*n;++i) ans=1ll*ans*ksm(i,c[i])%mod;
return !printf("%d",ans);
}
A组T1 JZOJ 6403 A
题目
分析
考虑容斥,即用总方案减去不合法的方案,若向
x
x
x轴正方向走
X
X
X步,向
y
y
y轴正方向走
Y
Y
Y步,向
z
z
z轴正方向走
Z
Z
Z步,记作
d
o
i
t
(
x
,
y
,
z
)
doit(x,y,z)
doit(x,y,z),那么方案数也就是
C
x
+
y
+
z
z
×
C
x
+
y
x
=
(
x
+
y
+
z
)
!
(
x
+
y
)
!
z
!
×
(
x
+
y
)
!
x
!
y
!
=
(
x
+
y
+
z
)
!
x
!
y
!
z
!
C_{x+y+z}^z\times C_{x+y}^x=\frac{(x+y+z)!}{(x+y)!z!}\times\frac{(x+y)!}{x!y!}=\frac{(x+y+z)!}{x!y!z!}
Cx+y+zz×Cx+yx=(x+y)!z!(x+y+z)!×x!y!(x+y)!=x!y!z!(x+y+z)!
设
f
[
i
]
f[i]
f[i]表示经过第
i
i
i个不可经过的点而没有经过前
i
−
1
i-1
i−1个不可经过的点的方案数
那么
f
[
i
]
=
d
o
i
t
(
x
i
,
y
i
,
z
i
)
−
∑
j
=
1
i
−
1
f
[
j
]
d
o
i
t
(
x
i
−
x
j
,
y
i
−
y
j
,
z
i
−
z
j
)
f[i]=doit(x_i,y_i,z_i)-\sum_{j=1}^{i-1}f[j]doit(x_i-x_j,y_i-y_j,z_i-z_j)
f[i]=doit(xi,yi,zi)−j=1∑i−1f[j]doit(xi−xj,yi−yj,zi−zj)
特殊地,把终点也当做不可经过的点已统计最终答案,因为坐标必须递增,所以先给不可经过的点升序排序,若这个不可经过的点无法到达下一个点,则不需转移
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int mod=1e9+7,N=3e5+5,M=5e4+5;
struct rec{int x,y,z;}a[M];
int fac[N],inv[N],n,m,lim,dp[M];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
bool cmp(const rec &a,const rec &b){
return a.x<b.x||(a.x==b.x&&(a.y<b.y||(a.y==b.y&&a.z<b.z)));
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline signed doit(int x,int y,int z){return 1ll*fac[x+y+z]*inv[x]%mod*inv[y]%mod*inv[z]%mod;}
signed main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n=iut(),m=iut(),fac[0]=fac[1]=inv[0]=inv[1]=1,lim=3*n;
for (rr int i=1;i<=m;++i) a[i].x=iut(),a[i].y=iut(),a[i].z=iut();
for (rr int i=2;i<=lim;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; a[++m]=(rec){n,n,n};
for (rr int i=2;i<=lim;++i) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
sort(a+1,a+1+m,cmp);
for (rr int i=1;i<=m;++i){
dp[i]=doit(a[i].x,a[i].y,a[i].z);
for (rr int j=1;j<i;++j){
if (a[j].x>a[i].x||a[j].y>a[i].y||a[j].z>a[i].z) continue;
dp[i]=mo(dp[i],mod-1ll*dp[j]*doit(a[i].x-a[j].x,a[i].y-a[j].y,a[i].z-a[j].z)%mod);
}
}
return !printf("%d",dp[m]);
}
A组T3 JZOJ 6405 C
题目
分析
根据一系列的找规律可以发现其实它在求
∑
i
=
1
n
∑
j
=
1
i
(
C
i
j
i
m
)
2
\sum_{i=1}^n\sum_{j=1}^i(C_i^ji^m)^2
i=1∑nj=1∑i(Cijim)2
=
∑
i
=
1
n
i
2
m
∑
j
=
1
i
C
i
j
2
=\sum_{i=1}^ni^{2m}\sum_{j=1}^i{C_{i}^j}^2
=i=1∑ni2mj=1∑iCij2
然后后面这一坨等于
C
2
i
i
C_{2i}^i
C2ii
=
∑
i
=
1
n
i
2
m
C
2
i
i
=\sum_{i=1}^ni^{2m}C_{2i}^i
=i=1∑ni2mC2ii
所以
O
(
n
)
O(n)
O(n)求解
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int mod=1e9+7,N=1e6+5;
int n,ans,fac[N<<1],jc[N],prime[N],cnt,m; bool v[N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline signed ksm(int x,int y){
rr int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
inline void pro(int lim){
jc[0]=jc[1]=fac[0]=fac[1]=1;
for (rr int i=2;i<=lim*2;++i) fac[i]=1ll*fac[i-1]*i%mod;
for (rr int i=2;i<=lim;++i){
if (!v[i]) prime[++cnt]=i,jc[i]=ksm(i,m);
for (rr int j=1;j<=cnt&&prime[j]*i<=lim;++j){
v[i*prime[j]]=1,jc[i*prime[j]]=1ll*jc[i]*jc[prime[j]]%mod;
if (i%prime[j]==0) break;
}
}
}
signed main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
n=iut(),m=(iut()<<1)%(mod-1),pro(n);
rr int inv=ksm(fac[n],mod-3);
for (rr int i=n;i;--i){
ans=mo(ans,1ll*jc[i]*mo(1ll*fac[i<<1]*inv%mod,mod-1)%mod);
inv=1ll*inv*i%mod*i%mod;
}
return !printf("%d",ans);
}