2019.01.05【NOIP提高组】模拟 B组

迟到的解题报告

  • JZOJ 3057 电影票
    • 题目大意
    • 分析
    • 代码
  • JZOJ 3058 火炬手
    • 题目
    • 分析
  • JZOJ 3059 雕塑
    • 题目
    • 分析
    • 代码

JZOJ 3057 电影票

题目大意

namb组成的序列,它的任意前缀 a的个数大于b的个数,这样的序列有多少个?


分析

首先它的答案是 c ( n + m , n ) − c ( n + m , n + 1 ) c(n+m,n)-c(n+m,n+1) c(n+m,n)c(n+m,n+1)
第一个很容易理解,n+m个选出n个当a
那第二个怎么解释,那应该是减掉不合法的,考虑一个由namb组成的不合法序列,肯定存在一个n=m-1的状态,这时只要转换a和b,使其变为m=n-1的状态,就可以使序列合法,那么当前序列是由n+1am-1b组成,所以说就是 c ( n + m , n + 1 ) c(n+m,n+1) c(n+m,n+1)
但是这样普通的高精乘除很慢,那么就要尽量优化,那么就是 n + 1 − m m c ( n + m , n + 1 ) = ( n + 1 − m ) ( n + m ) ! ( n + 1 ) ! m ! \frac{n+1-m}{m}c(n+m,n+1)=\frac{(n+1-m)(n+m)!}{(n+1)!m!} mn+1mc(n+m,n+1)=(n+1)!m!(n+1m)(n+m)!,可以提前对它们质因数分解,算出指数应该是多少,再用高精乘单精实现


代码

#include 
#define rr register
using namespace std;
typedef unsigned long long ull;
const int N=10000; const ull mod=(ull)1e12; bool flag;
int n,m,prime[N>>3],v[N+1],snt[N>>3],cnt,len; ull a[261];
inline void fj(int x,bool plmi){
	for (rr int i=1;i<=cnt&&x>1;++i)
	if (x%prime[i]==0)
	do{
		if (plmi) ++snt[i];
			else --snt[i];
		x/=prime[i];
	}while (x%prime[i]==0);
}
inline ull ksm(ull x,ull y){
	rr ull ans=1;
	while (y){
		if (y&1) ans*=x;
		x*=x; y>>=1;
	}
	return ans;
}
inline void mul(ull x){
	rr ull g=0;
	for (rr int i=1;i<=len;++i)
		g+=a[i]*x,a[i]=g%mod,g/=mod;
	while (g) a[++len]=g%mod,g/=mod;
}
inline void print(ull x){
	rr char buf[15]; rr int lenn=0;
	while (x) buf[++lenn]=x%10+48,x/=10;
	if (flag) for (rr int i=lenn+1;i<=12;++i) putchar(48);
	for (rr int i=lenn;i;--i) putchar(buf[i]); flag=1;
}
signed main(){
    for (rr int i=2;i<=N;++i){
		if (!v[i]) prime[++cnt]=i,v[i]=i;
		for (rr int j=1;j<=cnt;++j){
			if (prime[j]>N/i||prime[j]>v[i]) break;
			v[i*prime[j]]=prime[j];
		}
	}
	scanf("%d%d",&n,&m); a[++len]=1;
    if (n>m) fj(n+1-m,1);
	for (rr int i=n+2;i<=n+m;++i) fj(i,1);
	for (rr int i=2;i<=m;++i) fj(i,0);
	for (rr int i=1;i<=cnt;++i)
	if (snt[i]) mul(ksm(prime[i],snt[i]));
	for (rr int i=len;i;--i) print(a[i]);
	return 0;
}

JZOJ 3058 火炬手

题目

洛谷 1988 火炬 洛谷 2841 A*B Problem


分析

bfs 01串+余数优化(相同余数不会更优)


JZOJ 3059 雕塑

题目

将n座雕塑,准备安置在校园内,整个校园可以抽象成一个nn的大网格,每个11网格最多只能安置一座雕塑,但是某些1*1的网格上恰好是一个食堂或湖泊,这些网格是不能安置雕塑的,每个雕塑的造型相同,这样同一种安置方案中交换排列都算一种。任意雕塑在同一行或同一列是不合法的方案。


分析

由于不可以放的地方比较少,所以说可以用容斥原理 ∑ i = 1 m ( − 1 ) i × ( n − i ) ! × d f s ( 1 ∼ i 的 可 能 性 ) \sum_{i=1}^m(-1)^i\times (n-i)!\times dfs(1\sim i的可能性) i=1m(1)i×(ni)!×dfs(1i)


代码

#include 
#include 
#define rr register
using namespace std;
typedef unsigned long long ull;
ull fac[21]; int x[11],y[11],n,m; bool r[21],c[21]; 
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c==45)?-f:f,c=getchar();
	while (isdigit(c)) ans=ans*10+(c-48),c=getchar();
    return ans*f;
}
inline void dfs(int t,int dep,int &cnt){
	if (!dep) ++cnt;
	else for (rr int i=t+1;i<=m;++i)
	if (!r[x[i]]&&!c[y[i]]){
		r[x[i]]=c[y[i]]=1;
		dfs(i,dep-1,cnt);
		r[x[i]]=c[y[i]]=0;
	}
}
signed main(){
	n=iut(); m=iut(); rr ull ans=1; fac[0]=fac[1]=1;
	for (rr int i=1;i<=m;++i) x[i]=iut(),y[i]=iut();
	for (rr int i=2;i<=n;++i) fac[i]=(ans*=i);
	for (rr int i=1;i<=m;++i){
		rr int cnt=0; dfs(0,i,cnt);
		if (i&1) ans-=cnt*fac[n-i];
		    else ans+=cnt*fac[n-i];
	}
	printf("%llu",ans);
	return 0;
}

你可能感兴趣的:(模拟赛,模拟,搜索,容斥定理,JZOJ,3059,雕塑,JZOJ,3058,火炬手,洛谷,2841,A*B,Problem,洛谷,1988,火炬,JZOJ,3057,电影票)