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
m−k必须错排
而后面的
n
−
m
n-m
n−m个数,可错排,可不错排
于是可以列举后
n
−
m
n-m
n−m个数不错排的个数的情况,枚举范围[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=0n−md[n−k−i]∗Cn−mi)
注:
求错排的公式: 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;
}