JZOJ 3057 电影票
题目大意
由n个a和m个b组成的序列,它的任意前缀 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
那第二个怎么解释,那应该是减掉不合法的,考虑一个由n个a和m个b组成的不合法序列,肯定存在一个n=m-1的状态,这时只要转换a和b,使其变为m=n-1的状态,就可以使序列合法,那么当前序列是由n+1个a和m-1 个b组成,所以说就是
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+1−mc(n+m,n+1)=(n+1)!m!(n+1−m)(n+m)!,可以提前对它们质因数分解,算出指数应该是多少,再用高精乘单精实现
代码
#include <cstdio>
#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=1∑m(−1)i×(n−i)!×dfs(1∼i的可能性)
代码
#include <cstdio>
#include <cctype>
#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;
}