kuangbin数学训练2

LightOJ - 1058 Parallelogram Counting

题意

给出若干个点坐标,问最多能构成多少个不同的平行四边形

思路

找每条线段的中点重合即可
假如 m条线段的中点重合,则可构成 C m 2 C_m^2 Cm2个平行四边形

代码(转)

#include <cstdio>
#include <stack>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <map>
#include <vector>
#include <queue>
#include <set>
#define eps 1e-8
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 1e6;
const int INF = 0x3f3f3f;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
using namespace std;
int n;
struct node
{
    int x,y;
}p1[maxn+5],p2[maxn+5];
bool judge(int a, int b)
{
    if(p2[a].x == p2[b].x && p2[a].y == p2[b].y)
        return 1;
    return 0;
}
bool cmp(node a, node b)
{
    if(a.x==b.x)
        return a.y<b.y;
    return a.x<b.x;
}
int main()
{
    //ios::sync_with_stdio(false);
    int T;
    scanf("%d",&T);
    int tt = 1;
    while(T--)
    {
        scanf("%d",&n);
        for(int i = 0; i<n; i++)
            scanf("%d %d",&p1[i].x,&p1[i].y);
        int k = 0;
        //计算所有不同点对的中点
        for(int i = 0; i<n; i++)
            for(int j = i+1; j<n; j++)
        {
            p2[k].x = (p1[i].x+p1[j].x);
            p2[k++].y = (p1[i].y+p1[j].y);
        }
        ll cnt = 1,ans = 0;
        sort(p2,p2+k,cmp);
        for(int i = 1; i<k; i++)
        {
            if(judge(i,i-1))
                cnt++;
            else
            {
                ans += (cnt-1)*cnt/2;
                cnt = 1;
            }
        }
        ans += (cnt-1)*(cnt)/2;
        printf("Case %d: %lld\n",tt++,ans);
    }
 
    return 0;
}

LightOJ - 1067 Combinations

思路

求组合数模板,加深印象练手

代码

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

typedef long long LL;

const int N = 1000010, mod = 1000003;

int fact[N];
int infact[N];

int qmi(int a, int b, int mod) {
    int res = 1;
    while(b) {
        if(b & 1)
            res = (LL)res * a % mod;
        a = (LL)a * a % mod;
        b >>= 1;
    }

    return res;
}

void init() {
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++) {
        fact[i] = (LL)fact[i - 1] * i % mod;
        infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
}

int cal(int n, int m) {
    return (LL)fact[n] * infact[m] % mod * infact[n - m] % mod;
}

int main() {
    int id = 0;
    int T;
    scanf("%d", &T);
    init();
    while(T --) {
        int n, m;
        scanf("%d%d", &n, &m);
        int ans = cal(n, m);
        printf("Case %d: %d\n", ++ id, ans);
    }

    return 0;
}

LightOJ - 1095 Arrange the Numbers

题意

n个数1,2,3…n,问你前m个数中有只有k个数在原位上的方案数。

思路

组合数错排问题
首先前m个数k个在原位置的方案 C m k C_m^k Cmk
然后前m个数剩下的 m − k m-k mk必须错排
而后面的 n − m n-m nm个数,可错排,可不错排
于是可以列举后 n − m n-m nm个数不错排的个数的情况,枚举范围[0,n-m]
于是答案为
C n k ∗ ( ∑ i = 0 n − m d [ n − k − i ] ∗ C n − m i ) C_n^k*(\sum_{i=0}^{n-m}d[n-k-i]*C_{n-m}^{i}) Cnk(i=0nmd[nki]Cnmi)

注:
求错排的公式: f(n) = (n-1)*(f(n-1)+f(n-2))
证明

代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 1010, mod = 1000000007;

typedef long long LL;

int fact[N], infact[N];
int d[N];

int qmi(int a, int b, int mod) {
    int res = 1;
    while(b) {
        if(b & 1)
            res = (LL)res * a % mod;
        a = (LL)a * a % mod;
        b >>= 1;
    }

    return res;
}

void init() {
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++) {
        fact[i] = (LL)fact[i - 1] * i % mod;
        infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }

    d[0] = 1, d[1] = 0;
    for (int i = 2; i < N; i ++)
        d[i] = (LL)(i - 1) * (d[i - 1] + d[i - 2]) % mod;
}

int calc(int a, int b) {
    return (LL)fact[a] * infact[a - b] % mod * infact[b] % mod;
}

int main() {
    
    init();

    int T;
    int id = 0;
    scanf("%d", &T);
    while(T --) {
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        LL ans = 0;
        for (int i = 0; i <= n - m; i ++) {
            ans = (ans + (LL)d[n - k - i] * calc(n - m, i) % mod) % mod;
        }

        ans = (LL)ans * calc(m, k) % mod;

        printf("Case %d: %lld\n", ++id, ans);
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值