2019.11.04【NOIP提高组】模拟 A&B 组(部分)

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<<a2n1,所有的偶数项满足 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) a2i1a2i(1in)满足奇数项小于偶数项,即: a 2 i − 1 < a 2 i a_{2i-1}<a_{2i} a2i1<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 i1个不可经过的点的方案数
那么 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=1i1f[j]doit(xixj,yiyj,zizj)
特殊地,把终点也当做不可经过的点已统计最终答案,因为坐标必须递增,所以先给不可经过的点升序排序,若这个不可经过的点无法到达下一个点,则不需转移


代码

#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=1nj=1i(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=1ni2mj=1iCij2
然后后面这一坨等于 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=1ni2mC2ii
所以 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); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值