A-困难数学题
思路
\(x\) \(xor\) \(x\) \(xor\) \(x\) \(xor\) \(x\),异或有结合律,\(x\) \(xor\) \(x\) \(= 0\),\(0\) \(xor\) \(0\) \(= 0\),所以本题直接输出0。
代码
#include <iostream>using namespace std;int main()
{int x;cin >> x;cout << 0;return 0;
}
B-构造序列
思路
正数负数各取一个构造序列,直至某一个值消耗。可以知道,如果正数负数个数之差不超过1,直接输出 \(n + m\) 即可,超过1,则取较少的那个值乘2+1。
代码
#include <iostream>using namespace std;int main()
{int n, m;cin >> n >> m;if (abs(n - m) > 1) cout << min(n, m) * 2 + 1;else cout << n + m;return 0;
}
C-连点成线
思路
题意是求在一行或一列中最大差是多少。可以分别存 \(x,y\) 坐标,再分别跑 \(n\) 行和列,暴力求该行或列的最大差,取一个max值。时间复杂度为\(O(nmlogm)\),但有些行或列是可以不用求的,比如该行或列没有点或只有一个点。
代码
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;const int N = 1e5 + 10;int n, m;
vector<int> gx[N], gy[N];int main()
{cin >> n >> m;for (int i = 0; i < m; i ++){int x, y;cin >> x >> y;gx[x].push_back(y);gy[y].push_back(x);} int res = 0;for (int i = 1; i <= n; i ++){if (gx[i].size() > 1) {sort(gx[i].begin(), gx[i].end());res = max(res, gx[i].back() - gx[i].front());}if (gy[i].size() > 1) {sort(gy[i].begin(), gy[i].end());res = max(res, gy[i].back() - gy[i].front());}}cout << res;return 0;
}
D-我们N个真是太厉害了
思路
题目大意:给出的n个数是否可以组合1~n以内所有的数。
我的写法假了,但这题没卡,就是考虑dp,大小为i的数是否可以被构成,典型的01背包,但时间复杂度超了,我用了bitset进行优化。
代码
#include <iostream>
#include <bitset>using namespace std;const int N = 1e5 + 10;int n;void solve()
{cin >> n;bitset<N> st;st[0] = 1;for (int i = 0; i < n; i ++){int x;cin >> x;st |= (st << x);}int res= 0;for (int i = 1; i <= n; i ++){if (!st[i]) {res = i;break;}}if (!res) cout << "Cool!" << '\n';else cout << res << '\n';
}int main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int t;cin >> t;while (t --) solve();return 0;
}
真思路
将n个数排序后,前i项和\(s_i\)与第i+1项数\(a_{i+1}\)差值不超过1,那么1~\(s_i + a_{i+1}\)是可以被组成的。很容易思考,首先\(s_1\)必等于1,因为必须保证第一位要可组成,而组成1的数只有1,则在第二位,在满足第一位与第二位差值不超1的条件下2或3一定被组成,以此类推,第i个数时数值i一定可以组成,且前i项和也可被组成。
代码
#include <iostream>
#include <algorithm>using namespace std;typedef long long ll;const int N = 1e5 + 10;int n;
ll a[N];void solve()
{cin >> n;for (int i = 1; i <= n; i ++) cin >> a[i];sort(a + 1, a + 1 + n);ll sum = 0;for (int i = 1; i <= n; i ++)if (sum >= n) break;else if (sum + 1 < a[i]) {cout << sum + 1 << '\n';return ;}else sum += a[i];if (sum < n) {cout << sum + 1 << '\n';return ;}cout << "Cool!" << '\n';
}int main()
{int t;cin >> t;while (t --) solve();return 0;
}
E-折返跑
思路
组合数
n个点,两个杆一开始在1和n上,要绕杆跑m趟,每次可操作一次将左杆右移或右杆左移,在m趟后保证左右杆的方位不变。在去除1和n两个点后剩下n-2个点可以操作,但要求必须操作m-1次(最后一趟到达终点不必在操作),相当于将n-2个球取m-1次,求有多少种取法。那就是C(n-2, m-1)。
因为常规组合数写法是\(O(n^2)\)的,这里必须进行预处理优化,再用公式一步算出。
牛客题解给的组合数封装板子
代码
#include <iostream>using namespace std;typedef long long ll;const int M = 1e6 + 10;
const int mod = 1e9 + 7;int n, m;
ll fact[M + 10], infact[M + 10];ll qmi(int a, int b)
{ll res = 1;while(b){if(b & 1) res = res * a % mod;a = (ll)a * a % mod;b >>= 1;}return res;
}void init()
{fact[0] = infact[0] = 1;for(int i = 1; i <= M; i ++) fact[i] = fact[i - 1] * i % mod;infact[M] = qmi(fact[M], mod - 2);for(int i = M - 1; i >= 1; i --) infact[i] = infact[i + 1] * (i + 1) % mod;
}int C(int a, int b)
{if(b < 0 || a - b < 0) return 0;return fact[a] * infact[b] % mod * infact[a - b] % mod;
}void solve()
{cin >> n >> m;cout << C(n - 2, m - 1) << '\n';
}int main()
{init();int t;cin >> t;while (t --) solve();return 0;
}
F-口吃
牛客官方题解
代码
#include <iostream>using namespace std;typedef long long ll;const int mod = 1e9 + 7;
const int N = 1e5 + 10;int n;
ll a[N], b[N], s[N];
ll f[N];ll qmi(ll a, ll b)
{ll res = 1;while (b){if (b & 1) res = res * a % mod;a = a * a % mod;b >>= 1;}return res;
}ll inv(ll x)
{return qmi(x, mod - 2);
}int main()
{cin >> n;for (int i = 1; i < n; i ++) cin >> a[i];for (int i = 1; i < n; i ++) cin >> b[i];for (int i = 1; i < n; i ++) s[i] = (a[i] + b[i]) % mod;f[1] = s[1] * inv(a[1]) % mod;ll res = f[1];for (int i = 2; i < n; i ++){f[i] = (b[i] * b[i] % mod * f[i - 1] % mod + s[i] * s[i] % mod) % mod * inv(a[i] * a[i] % mod) % mod;res = (res + f[i]) % mod;}cout << res + 1;return 0;
}