暑假集训_排列组合
A Tourist Problem
题意
给定一条直线上n个点的坐标 a1,a2,...an,一条路线从原点开始,经过a的一个排列并在最后一个点结束(也就是说不返回原点)。
一条路径的长度定义为排列中相邻两点x,y的距离∣x−y∣(包括原点),求所有路线的平均长度,要求化为最简分数。
思路
通过打表找规律可以的到,任意两个数相减共有 (n-1)! 个。
对数组排好序,以 1 2 3 4 5 为例
元素1 被 2 3 4 5每个减了2次,它自己减0一次;贡献值为 (-7 * 1);
元素2 被 3 5 4 每个减了2次,它减1两次,减0一次;贡献值为 (-3 * 2);
元素3 贡献值为( 1 * 3 );
可以发现他们前面乘的数相差为4,找到这个规律可以的如下公式
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e5, mod = 1e9 + 7; int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } void solve() { int n; cin >> n; int s[n + 10]; for (ll i = 1; i <= n; i++) cin >> s[i]; ll t = -n * 2 - 1, m = 0; sort(s + 1, s + n + 1); for (ll i = 1; i <= n; i++) m += s[i] * (t + i * 4); ll x = gcd(m, n); cout << m / x << " " << n / x << endl; } int main() { cin.tie(0), cout.tie(0); ios::sync_with_stdio(0); // int T; // cin >> T; // while (T--) solve(); return 0; }
B Bargain
思路
首先我们很明显可以按位考虑,由于删除的数是连续的,所以如果当前这一位被删去了的话肯定就没贡献,所以分类讨论每一位:
- 删去的数是前面的一段连续数
- 删去的数是后面的一段连续数
- 删去的数包含了当前位,此时没贡献
我们首先看只删前面,此时我们发现不管前面怎么删,此时我们当前位的贡献是固定的,所以只要知道前面有多少种删法就可以了,这个很明显 i×(i−1)/2 。
接着我们看只删后面,很类似前面,不过这里要考虑后面剩余位数对答案的影响,如果只剩一位就 1 种情况,两位就 2 种情况,把 00 提到我们计算数里面就是 1 , 20 , 300…… 从后往前算的时候累加一下这玩意儿就好了。
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e5, mod = 1e9 + 7; void solve() { string s; cin >> s; ll n = s.size(); s = " " + s; ll k = 1, sum = 0, ans = 0; for (ll i = n; i >= 1; i--) { ll m = (i - 1) * i / 2; ans = (ans + (k * m % mod * (s[i] - '0')) % mod) % mod; ans = (ans + sum * (s[i] - '0') % mod) % mod; sum = (sum + (n - i + 1) * k % mod) % mod; k = k * 10 % mod; } cout << ans; } int main() { cin.tie(0), cout.tie(0); ios::sync_with_stdio(0); // int T; // cin >> T; // while (T--) solve(); return 0; }
C Intercity Travelling
思路
首先 p⋅2n-1 即期望乘上全部可能,就是各个可能的困难度的和。
可以用数学方法(打表找规律).......
得到式子:

代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e5, mod = 998244353; void solve() { int n; cin >> n; ll s[n + 10]; s[0] = 1; for (int i = 1; i <= n; i++) s[i] = s[i - 1] * 2 % mod; ll a[n + 1]; for (int i = 1; i <= n; i++) cin >> a[i]; ll ans = a[n]; for (ll i = 1; i < n; i++) ans = (ans + (a[i] * (s[n - i] + s[n - i - 1] * (n - i) % mod) % mod) % mod) % mod; cout << ans % mod << endl; } int main() { cin.tie(0), cout.tie(0); ios::sync_with_stdio(0); // int T; // cin >> T; // while (T--) solve(); return 0; }
D [CQOI2014]数三角形
思路
任取三个点得到所有的组合数再减去不能构成三角形的数也即三点共线的数量,即可得到答案。
具体求法是先求出水平共线数量与竖直共线数量。
用排列组合的知识可知为接着处理倾斜的直线,直接枚举直线起点终点无疑要超时,注意到除去水平与竖直直线后,每一条直线都是有斜率的。故只需要枚举倾斜直线的倾斜向量,然后便可以使用最大公约数/排列组
合的知识解决了。
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e5, mod = 998244353; ll chack(ll x) { return x * (x - 1) * (x - 2) / 6; } void solve() { int n, m; cin >> n >> m; ll ans = chack((n + 1) * (m + 1)); ans = ans - chack(n + 1) * (m + 1) - chack(m + 1) * (n + 1); for (int i = 1; i <= n; i++) //枚举倾斜向量 for (int j = 1; j <= m; j++) ans -= (__gcd(i, j) - 1) * (m - j + 1) * (n - i + 1) * 2; //有(n-i+1)*(m-j+1)个可选向量,中间点有gcd-1种可选方案,上下翻转*2 cout << ans; } int main() { cin.tie(0), cout.tie(0); ios::sync_with_stdio(0); // int T; // cin >> T; // while (T--) solve(); return 0; }

浙公网安备 33010602011771号