[HNOI2012] 排队(高精度+组合数学)

[HNOI2012] 排队

题目描述

某中学有 n n n 名男同学, m m m 名女同学和两名老师要排队参加体检。他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的)

输入格式

只有一行且为用空格隔开的两个非负整数 n n n m m m,其含义如上所述。

输出格式

仅一个非负整数,表示不同的排法个数。注意答案可能很大。

样例 #1

样例输入 #1

1  1

样例输出 #1

12

提示

对于 30 % 30\% 30% 的数据 n ≤ 100 n\leq 100 n100 m ≤ 100 m\leq 100 m100

对于 100 % 100\% 100% 的数据 n ≤ 2000 n\leq 2000 n2000 m ≤ 2000 m\leq 2000 m2000

思路

  • 首先,这道题我们可以分成两个条件(因为直接算很难),条件1:任意两名女同学不能相邻。条件2:两名老师也不能相邻。
  • 此时,用到高中容斥原理,要让条件1和条件2同时成立,我们可以先让条件1成立得到的答案设为 a n s ans ans,然后把 a n s ans ans减去条件1成立,条件2成立(设为 r e s res res)。(这样很好做,我高中的时候经常这么玩)
  • 然后对于 a n s ans ans的计算,我们可以很快的算出: A n + 3 m × A n + 2 n + 2 A_{n+3}^{m} \times A_{n+2}^{n+2} An+3m×An+2n+2
  • r e s res res的计算,可以是: 2 × A n + 1 n + 1 × A n + 2 m 2\times A_{n+1}^{n+1}\times A_{n+2}^{m} 2×An+1n+1×An+2m
  • 答案为二者之差。
  • 注意: r e s res res中的 m ≤ n + 3 m\le n+3 mn+3并且 a n s ans ans中的 m ≤ n + 2 m\le n+2 mn+2。(不这样可能会出错)
    但注意,本道题得使用高精度x高精度的乘法。
    先附上模板:
vector<int> mul(vector<int> A, vector<int> B) {
    vector<int>C(A.size()+B.size()+100,0);
    
    int t = 0;

    for(int i=0;i<A.size();i++) {
        for(int j=0;j<B.size();j++) {
            C[i+j]+=A[i]*B[j];
        }
    }

    for(int i=0;i<C.size();i++) {
        t+=C[i];
        C[i]=t%10;
        t/=10;
    }

    while(C.size()>1&&C.back()==0)C.pop_back();
    return C;
}

那么这样这道题就很好做了。

代码

//高精度+组合数学

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

int n,m;

vector<int> add(vector<int> a,vector<int> b){
    int t=0;
    
    vector<int>res;
    
    for(int i=0;i<a.size()||i<b.size();i++){
        if(i<a.size())t+=a[i];
        if(i<b.size())t+=b[i];
        res.push_back(t%10);
        t/=10;
    }
    
    if(t)res.push_back(1);
    
    return res;
}

vector<int> sub(vector<int> a,vector<int> b){
    int t=0;
    vector<int>res;
    
    for(int i=0;i<a.size();i++){
        t=a[i]-t;
        if(i<b.size())t-=b[i];
        res.push_back((t+10)%10);
        if(t<0)t=1;
        else t=0;
    }
    
    while(res.size()>1&&res.back()==0)res.pop_back();
    
    return res;
}

//高精度x低精度
vector<int> mul(vector<int> a,int b){
    int t=0;
    
    vector<int>res;
    
    for(int i=0;i<a.size()||t;i++){
        if(i<a.size())t+=a[i]*b;
        res.push_back(t%10);
        t/=10;
    }
    
    return res;
}
//高精度x高精度
vector<int> mul(vector<int> A, vector<int> B) {
    vector<int>C(A.size()+B.size()+100,0);
    
    int t = 0;

    for(int i=0;i<A.size();i++) {
        for(int j=0;j<B.size();j++) {
            C[i+j]+=A[i]*B[j];
        }
    }

    for(int i=0;i<C.size();i++) {
        t+=C[i];
        C[i]=t%10;
        t/=10;
    }

    while(C.size()>1&&C.back()==0)C.pop_back();
    return C;
}


vector<int> A(int a,int b){
    vector<int> k;
    k.push_back(1);
    for(int i=a,j=b;i>=1;i--,j--){
        k=mul(k,j);
    }
    
    return k;

}

vector<int> a,b,c,d,e,f,g;

int main(){
    cin>>n>>m;
    if(n+3>=m){//这两个限制条件
        a=A(m,n+3);
        b=A(n+2,n+2);
        c=mul(a,b);
        g=add(g,c);//g+=c;
    }
    
    if(n+2>=m){
        d=mul(A(n+1,n+1),2);
        e=A(m,n+2);
        f=mul(d,e);
        g=sub(g,f);//g-=f;
    }
    
    for(int i=g.size()-1;i>=0;i--){
        cout<<g[i];
    }
    
    return 0;
}

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值