Codeforces Round #818 (Div. 2)
Problem A
题意:对于 ,求
的总数
思路:考察gcd和lcm的定义,gcd是a,b素因子的交集,lcm是a,b素因子的并集,相当于是lcm ∩ gcd,也就是说a与b之间除了素因子w(w=1,2,3),其他的素因子相同,所以a=b*w(a>=b)
#include<iostream> #include<cstring> using namespace std; using LL = long long; int main(){ cin.tie(0); cout.tie(0); ios::sync_with_stdio(0); int T; cin >> T; while(T--){ int n; cin >> n; cout << n + 2 * (n / 2 + n / 3) << '\n'; } }
Problem B
题意:一个n*n的矩形,我们需要往矩形上填充‘X’,保证每行每列至少有一个‘X’,同时n是k的倍数,我们还需要保证任意一个1*k和k*1的矩形中包含一个‘X’,另外pos(r,c)必须是‘X’,询问最少‘X’的数量。
思路: 1.首先我们先不考虑pos(r,c)和小矩形限制,对于任意正方形满足行列至少有一个‘X’,
将对角线填充上‘X’就是最优解,我把他叫做对角线推论。
2.然后考虑将n*n矩形分割为k*k矩形,对角线推论任然满足。
3.下一步考虑加入pos(r,c),对于这种情况,为了继续满足对角线推论,这个小的子矩形我们任然按照推论填充,保证子矩形最优解。
4.然后与这个子矩形相对的子矩形我们同样按照这个最优策略,这样就可以保证最后的结果是最优的。
5.最后为了满足1*k和k*1的小矩形限制,只需要将对角线向两边平移即可
#include<bits/stdc++.h> using namespace std; int t, n, k, r, c; void solve() { cin >> n >> k >> r >> c; r--, c--; int dis = (r + c) % k;//实际处理对角线平移技巧,将从1开始编号的矩阵改为从0开始,如果(r+c)%k=(a+b)%k,则(a,b)处于平移对角线上 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { int _dis = (i + j) % k; if (_dis == dis) cout << "X"; else cout << "."; } cout << endl; } } int main() { cin >> t; while (t--) { solve(); } }
Problem C
题意:给出a数列和b数列,如果能将a数列变化为b数列输出“YES”,否则输出“NO”
变换规则是 ai:=ai+1 if i<ni<n and ai≤ai+1, or i=n and ai≤a1
思路:比赛时已经分析出了
1.a序列可以变为任意满足条件3的b序列
2. ai>bi无解
3. b序列中,|b(n)-b(n+1)|>=2则无解
但是第三条结论有漏洞应当更正为:ai!=bi, bi>b(i+1) +1无解,因为这会导致第i位被限制到b(i+1)-1
考虑一下如果要求方案该如何求
在满足上述条件的情况下如果ai=bi,那么该位满足
如果ai<bi,ai>aj,我们需要首先增加aj,因为ai的增加受到aj的限制
如果ai<bi,ai<=aj,那么直接搞就好
可以发现这跟拓扑排序十分相似,只有出现第二种情况时才会对其他的操作施加前置 限制,所以当出现第二种情况时,我们在图上连上一条j——>i的边
同时因为我们推断出了a数列可以变化为任意满足条件3的b数列,这个图是无环的,满足拓扑排序条件
#include<bits/stdc++.h> using namespace std; int t, n; const int N = 2e5 + 10; int a[N], b[N]; void solve() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) cin >> b[i]; bool flag = true; b[n + 1] = b[1];//要判断b[n]和b[1]之间的关系,但如果(i+1)=n取模需要特判,为了省事直接接一个b[1]在后面 for (int i = 1; i <= n; i++) { if (a[i] > b[i]) flag = false; if (a[i] != b[i] && b[i] > b[i + 1] + 1) flag = false; if (!flag) break; } if (flag) cout << "YES" << endl; else cout << "NO" << endl; } int main() { cin >> t; while (t--) { solve(); } }
Problem D
题意:2^n个人两两比赛,胜者晋级并两两之间进行下一轮比赛,按照此规则决出冠军
Madoka有修改比赛结果的权利,最后决出的冠军编号越小,他的收益就会越大;
但是同时赞助商有着k次修改已选定比赛结果的权利,最后冠军的编号越大赞助商收益越大。
询问双方按照各自的最优策略选择,最后的冠军编号是多少
思路:双方策略
Madoka:最后的冠军编号小
赞助商:最后的冠军编号大
我们发现双方的目的关联性比较小,但他们其实等价于
Madoka:使得赞助商选不到较大的冠军编号
赞助商:最后的冠军编号大
进一步的,赞助商的选定优先级是2^n, 2^(n-1), 2^(n-2)……
所以Madoka的策略是让得到较大编号的修改次数尽可能多,最大修改次数为n次
又因为所有的选择情况为
如何让一些选手不可能获胜呢?我们知道n个人中一定有n / 2个人刚开始就失败了,因此我们让编号最大的人直接失败,到下一层也是。如何选出这些人的数量呢?因为一共要进行n次比赛,我们只需要统计出某些人失败的次数 > k的人数,把这些人都用最大编号的人来排即可。
我们一遍for(int i = k + 1 ; i <= n ; i ++ )循环,每次有C(n, i)的人永远不可能胜利。
#include<bits/stdc++.h> using namespace std; #define int long long const int mod = 1e9 + 7; int n, k, op; int qpow(int x) { int ans = 1, q = 2; while (x) { if (x & 1) ans = ans * q % mod; q = q * q % mod; x >>= 1; } return min(mod, ans); } int C(int x, int y) { if (y > x / 2) y = x - y; int a = 1, b = 1; for (int i = 1; i <= y; i++) { a = a * (x + 1 - i) % mod; b = b * i % mod; if (a % b == 0) a /= b, b = 1; } return a / b; } void solve() { cin >> n >> k; op = qpow(n); if (k >= n) { cout << op; return; } int w = C(n, k); for (int i = k + 1; i <= n; i++) { w = w * (n - i + 1) % mod;//节约时间 w = w / i; op = ((op - w) % mod + mod) % mod; } cout << op; return; } signed main() { std::ios::sync_with_stdio(false);//加快cin读取速度的 cin.tie(0); solve(); return 0; }//只过了7个点
Problem E
核心思想就是: gcd(a,b)=1, a+b=c ——>gcd(a,c)=1
#include<iostream> #include<cstring> #include<vector> #include<algorithm> using namespace std; using LL = long long; const int maxn = 1e5 + 5, mod = 1e9 + 7; template<int T> struct ModInt { const static int mod = T; int x; ModInt(int x = 0) : x(x% mod) {} int val() { return x; } ModInt operator + (const ModInt& a) const { int x0 = x + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); } ModInt operator - (const ModInt& a) const { int x0 = x - a.x; return ModInt(x0 < mod ? x0 + mod : x0); } ModInt operator * (const ModInt& a) const { return ModInt(1LL * x * a.x % mod); } ModInt operator / (const ModInt& a) const { return *this * a.inv(); } void operator += (const ModInt& a) { x += a.x; if (x >= mod) x -= mod; } void operator -= (const ModInt& a) { x -= a.x; if (x < 0) x += mod; } void operator *= (const ModInt& a) { x = 1LL * x * a.x % mod; } void operator /= (const ModInt& a) { *this = *this / a; } friend ostream& operator<<(ostream& os, const ModInt& a) { return os << a.x; } ModInt pow(LL n) const { ModInt res(1), mul(x); while (n) { if (n & 1) res *= mul; mul *= mul; n >>= 1; } return res; } ModInt inv() const { int a = x, b = mod, u = 1, v = 0; while (b) { int t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v); } if (u < 0) u += mod; return u; } }; typedef ModInt<mod> mint; bool isPrime[maxn]; int phi[maxn]; int primes[maxn], cnt; void init() { phi[1] = 1; for (int i = 2; i < maxn; i++) { if (!isPrime[i]) primes[cnt++] = i, phi[i] = i - 1; for (int j = 0; i * primes[j] < maxn; j++) { isPrime[i * primes[j]] = 1; if (i % primes[j] == 0) { phi[i * primes[j]] = phi[i] * primes[j]; break; } phi[i * primes[j]] = phi[i] * (primes[j] - 1); } } } LL __gcd(LL a, LL b) { if (b) return __gcd(b, a % b); else return a; } int main() { cin.tie(0); cout.tie(0); ios::sync_with_stdio(0); auto lcm = [&](int a, int b) { return mint(a / __gcd(a, b)) * mint(b); }; init(); int n; cin >> n; mint ans = 0; for (int i = 1; i <= n; i++) for (int j = 2 * i; j < n; j += i) ans += lcm(i, n - j) * mint(phi[j / i]); cout << ans << '\n'; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】