机房测试5:silhouette(组合数+递推)

题目:

分析:

(这道题是真的难)(声明: 在这位大佬的题解下多做了说明,图片来源也是他的博客。)

首先我们要发现一些小规律:

1.将A和B排序之后并不影响答案

证明:不管哪一列排序放到了哪里,那一列的最大值都应该是Ai。

2.A的最大一定等于B的最大:

很显然,如果不等于,那么最大值放在哪里都不合法。

3.将A和B数组按从小到大的顺序排序后,会变成这种矩形:

定义f[ i ]为至少有i行不满足,定义至少的原因:两两行之间不会受影响

显然最后我们要求的是恰好有0行不满足,即至少有0行不满足-至少有1行不满足,但因为至少有一行不满足中包含了至少有两行不满足,所以要加上,有加多了至少有三行不满足的情况,以此类推,用容斥原理计算答案。

先推S1的情况,再在S1的基础上得到S2的公式。

公式的解释他写的太好了,这里就不再阐述。

#include<bits/stdc++.h>
using namespace std;
#define ri register int
#define ll long long
#define N 100005
const ll mod = 1e9+7;
ll a[N],b[N],s[N<<1],invfac[N],fac[N];
int read()
{
    int x=0,fl=1; char ch=getchar();
    while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) fl=-1; ch=getchar(); }
    while(ch<=‘9‘&&ch>=‘0‘) x=x*10+ch-‘0‘,ch=getchar();
    return fl*x;
}
ll quick_pow(ll a,ll k)
{
    ll ans=1;
    while(k) { if(k&1) ans=ans*a%mod; a=a*a%mod; k>>=1; }
    return ans;
}
void init(int n)
{
    fac[0]=1;
    for(ll i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod;
    invfac[n]=quick_pow(fac[n],mod-2);
    for(ll i=n;i>=1;--i) invfac[i-1]=invfac[i]*i%mod;
}
ll C(ll m,ll n)
{
    return fac[n]*invfac[m]%mod *invfac[n-m] %mod;
}
ll calc(ll a,ll b,ll c,ll d,ll ss)
{
    ll ret=0;
    for(ll i=0;i<=a;++i){
        ll tmp=C(i,a)* quick_pow(( quick_pow(ss,i) * ( quick_pow(ss+1,a+c-i) - quick_pow(ss,a+c-i) +mod) %mod),b) %mod;
        tmp=tmp * quick_pow(( quick_pow(ss,i) * quick_pow(ss+1,a-i) %mod ),d) %mod;
        if(i&1) ret=(ret-tmp+mod)%mod;
        else ret=(ret+tmp)%mod;
    }
    return ret;
}
int main()
{
    freopen("silhouette.in","r",stdin);
    freopen("silhouette.out","w",stdout);
    int n=read(),cnt=0;
    for(ri i=1;i<=n;++i) a[i]=read(),s[++cnt]=a[i];
    for(ri i=1;i<=n;++i) b[i]=read(),s[++cnt]=b[i];
    sort(a+1,a+1+n);
    sort(b+1,b+1+n);
    if(a[n]!=b[n]) { printf("0\n"); return 0; }
    sort(s+1,s+1+cnt);
    int num=unique(s+1,s+1+2*n)-s-1;
    int na=n,nb=n,pa=n+1,pb=n+1;
    init(n);
    ll ans=1;
    for(ri i=num;i>=1;--i){
        while(na-1 && a[na-1]==s[i]) na--;
        while(nb-1 && b[nb-1]==s[i]) nb--;
        ans=ans* calc( pa-na,pb-nb,n-pa+1,n-pb+1,s[i] ) %mod;
        pa=na; pb=nb;
    }
    printf("%lld\n",ans);
}
/*
2
1 2
2 1
*/

原文地址:https://www.cnblogs.com/mowanying/p/11625572.html

时间: 2024-10-10 14:36:12

机房测试5:silhouette(组合数+递推)的相关文章

组合数递推算法

主要式子:C(n,k)=C(n-1,k-1)+C(n-1,k),C(n,k)表示从n个物品中挑选k个物品的所有组合数. 1 #include<stdio.h> 2 #include<string.h> 3 #define N 10 4 int c[N][N]; 5 void init() 6 { 7 memset(c,0,sizeof(c)); 8 for(int i=0;i<=N;i++) 9 { 10 c[i][0]=1; 11 c[i][i]=1; 12 for(int

Codeforces 631 D. Dreamoon Likes Sequences 位运算^ 组合数 递推

https://codeforces.com/contest/1330/problem/D 给出d,m, 找到一个a数组,满足以下要求: a数组的长度为n,n≥1; 1≤a1<a2<?<an≤d: 定义一个数组b:b1=a1, ∀i>1,bi=bi−1⊕ai ,并且b1<b2<?<bn−1<bn: 求满足条件的a数组的个数并模m: 人话:求一个a数组满足递增,并且异或前缀和也递增 ,求出a数组个数mod m. 太菜了,不会,看了很多题解才会的,这里总结一下:

hdu2068 RPG的错排 组合数/递推

1 #include<stdio.h> 2 long long arr[21]; 3 long long c(int a,int b) 4 { 5 long long i,sum=1,j; 6 for (i=a,j=1;i>=a-b+1,j<=b;i--,j++) sum=sum*i/j; 7 return sum; 8 } 9 void func() 10 { 11 int i; 12 arr[1]=0;arr[2]=1; 13 for(i=3;i<=26;i++) 14

UVa 10253 (组合数 递推) Series-Parallel Networks

<训练之南>上的例题难度真心不小,勉强能看懂解析,其思路实在是意想不到. 题目虽然说得千奇百怪,但最终还是要转化成我们熟悉的东西. 经过书上的神分析,最终将所求变为: 共n个叶子,每个非叶节点至少有两个子节点的 树的个数f(n).最终输出2 × f(n) 首先可以枚举一下根节点的子树的叶子个数,对于有i个叶子的子树,共有f(i)种, 设d(i, j)表示每棵子树最多有i个叶节点,一共有j个叶节点的方案数. 所求答案为d(n-1, n) 假设恰好有i个叶子的子树有p棵,因为每个子树互相独立,所以

【UVA】12034-Race(递推,组合数打表)

递推公式,假设第一名有i个人并列,那么: f[n] = C(n,i) * f[n - i]; 打出1 ~ 1000的所有组合数,之后记忆化搜索,需要打表. 14026995 12034 Race Accepted C++ 0.032 2014-08-12 11:47:47 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector&g

递归--递推之组合数

排列在上一篇中已经写到,是个典型的深搜题,下面是介绍的组合数, 组合的基本定义是, 但是除了用这种传统的方法来求,可以用递归的方式或者是递推的方式来求, 说道递推,只要会递归, 就会递推了.关键的一部是递推式,可以定义一个函数func(int n, int k); 表示求的值,公式先放在这func(n, k) = func(n-1, k-1)+func(n-1,k); 意思就是在n中选去k个数的组合一个多少个,这时就要分两种情况, 一种是选出一组数中包含最后一个元素,它的值就是func(n -

Uva 10943 - How do you add ?( 组合数公式 + 递推 )

Uva 10943 - How do you add ?( 组合数公式 + 递推 ) 题意:给定一个数N,分解韡k个数,问有多少种组合.数可以重复 (N<=100) 分析:通过组合数学将题目抽象为 N个相同的小球,放入k个不同的盒子中,且允许盒子为空然后就可以用隔板法------> ANS = C(N+M-1,M-1)隔板法--> BD然后再根据组合数的性质可以进行递推(这题数据范围比较小)----> C(A,B) = C(A-1,B) + C(A-1,B-1);预处理一遍之后即可

SDUT 2241-计算组合数C(n,m)(递推)

题目链接:点击打开链接 线性O(n) 计算组合数,递推法. #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <string> #include <cctype> #include <vector> #include <cstdio> #include <cmath> #in

Gym10081 A - Arcade Game -康托展开、全排列、组合数变成递推的思想

最近做到好多概率,组合数,全排列的题目,本咸鱼不会啊,我概率论都挂科了... 这个题学到了一个康托展开,有点用,瞎写一下... 康托展开: 适用对象:没有重复元素的全排列. 把一个整数X展开成如下形式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0![1] 其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]<i(1<=i<=n) 用来求全排列中这个串排第几,康托展开的逆运算就是