CW寒假年前集训1.20(Codeforces Round 998 div.3)
T1:
这应该不需要解析思路了吧
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
int a[6];
void solve(){
std:: cin >> a[1] >> a[2] >> a[4] >> a[5];
int sum1 = a[1] + a[2];
int sum2 = a[4] - a[2];
int sum3 = a[5] - a[4];
if(sum1 == sum2 && sum2 == sum3) std::cout << 3 << '\n';
else if(sum1 == sum2 || sum2 == sum3 || sum1 == sum3) std::cout << 2 << '\n';
else std::cout << 1 << '\n';
}
int main(){
int T;
std::cin >> T;
while(T--) solve();
return 0;
}
T2:
题意:
\(n\times m\)个牌分在\(n\)头牛手里,每人手里\(m\)个,要有一个排列使得按照这个排列上的顺序出牌,让所有人都可以出完,出牌规则是要大于上一个人出的牌。
思路:
我们可以找到 \(0∼n−1\)对应的牛牛,这就是出牌顺序,接下来\(n∼n\times m−1\)都按这个牛牛顺序出牌,如果有人没有这张牌就是无解情况,否则直接输出最开始的出牌顺序即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
void solve(){
int n, m;
std::cin >> n >> m;
std::vector<int> a(n * m);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
int x;
std::cin >> x;
a[x] = i;
}
}
std::vector<int> ans(n), last(n, -n);
for (int i = 0; i < n * m; ++ i) {
if (i - last[a[i]] < n) {
std::cout << -1 << "\n";
return;
}
last[a[i]] = i;
if (i < n) {
ans[i] = a[i];
}
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] + 1 << " \n"[i == n - 1];
}
}
int main(){
int T;
std::cin >> T;
while(T--) solve();
return 0;
}
T3:
题意:
有\(n\)个数,每次\(Alice\)选一个数\(a\),\(Bob\)选一个数\(b\),如果\(a+b=k\),那么分数加一,问最终分数。
思路:
分数是固定的,因为如果牌库里有\(k - a\)那么\(Bob\)一定会选。那么计算每个\(a\)和\(k - a\)能产生的贡献即可。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
void solve(){
int n,k;
std::cin >> n >> k;
std::vector<int> cnt(2 * n + 1);
for (int i = 1; i <= n;++i) {
int x;
std::cin >> x;
++cnt[x];
}
int ans = 0;
for (int i = 1; i <= k / 2;++i) {
if (k % 2 == 0 && i == k / 2) ans += cnt[i] / 2;
else ans += std::min(cnt[i], cnt[k - i]);
}
std::cout << ans << '\n';
}
int main(){
int T;
std::cin >> T;
while(T--) solve();
}
T4:
题意:
一个数组\(a\),你可以让两个相邻的数一起减去他们之中的最小值(可执行任意次)。问能不能使数组非递减。
思路:
假设\([1,i]\)已经非递减,那么如果\(a_{i} > a_{i+1}\),那么操作\(i\)和\(i+1\)是无效的,现在只能考虑\(i\)和\(i-1\),所以让\(a_{i} = a_{i} - a_{i-1}\)。若\(a_{i} \le a_{i+1}\),若我们不操作的话,遇到\(a_{i + 1} > a_{i + 2}\)那么只操作\(i,i+1\)是不够的,因为这样后\(a_{i} = 0\),而此时\(a_{i} < a_{i - 1}\)所以还是要让\(a_{i} = a_{i} - a_{i - 1}\)。若后面无递减情况,那么我们这样操作也不会影响数组的单调性。所以每次都让\(a_{i} = a_{i} - a_{i - 1}\)一定是更优的。
最后不要忘了特判\(a_{1} > a_{2}\)的情况。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
void solve(){
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for(int i = 1;i <= n;++i) std::cin >> a[i];
if(a[1] > a[2]) {
std::cout << "NO" << '\n';
return;
} //若a[1] > a[2],由不等式的性质,其无论如何都不可能可行
for(int i = 2;i + 1 <= n;++i){
a[i] -= a[i - 1];
if((a[i] > a[i + 1])) {
std::cout << "NO" << '\n';
return;
}
}/*假定[1,i]已经非递减,若a[i] < a[i + 1],则依旧满足非递减的性质,若a[i] > a[i + 1],则调整a[i] 与 a[i + 1] 的大小是无效的
则考虑a[i] > a[i - 1]的关系,显然,让a[i] 下调后依旧不能满足题意(因为此时a[i - 1]已经变成0),那么一定不可能用题所给操作使序列不递减
*/
std::cout << "YES" << '\n';
return;
}
int main(){
int T;
std::cin >> T;
while(T--) solve();
return 0;
}
T5:
题意:
给你两个图\(f,g\),你可以每次操作给\(f\)加一条边或者减一条边。问让两个图任意两个点联通性相同需要的最小操作数
思路:
这是一道连通性问题,我们考虑用并查集来维护这两个图。
形式化的:我们用\(f_{u}\)与\(g_{u}\)来表示点u在两个图分别所在的集合(联通块)
首先,若\(Edge(u,v) \in f\)且\(g_{u} \neq g_{u}\),那么这条边一定要删。
接着,我们枚举图\(g\)的每个集合(联通块),若\(\exists v \in g_{u}且v \notin f_{u}\),那么显然\(f\)需要加一条边,同时在图\(f\)的集合里合并\(f_{u}\)与\(f_{v}\),最后,别忘了每执行一次操作就让答案贡献加1
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
const int N = 2e5 + 5;
int fa1[N],fa2[N];
int findF(int x){
return (fa1[x] == x ? x : fa1[x] = findF(fa1[x]));
}
void mergeF(int x,int y){
x = findF(x),y = findF(y);
if(x == y) return;
fa1[y] = x;
}
int findG(int x){
return (fa2[x] == x ? x : fa2[x] = findG(fa2[x]));
}
void mergeG(int x,int y){
x = findG(x),y = findG(y);
if(x == y) return;
fa2[y] = x;
}
void solve(){
int n,m,k;
std::cin >> n >> m >> k;
for(int i = 1;i <= n;++i) fa1[i] = i,fa2[i] = i;
std::vector<int> u1(m + 1),v1(m + 1),u2(k + 1),v2(k + 1);
for(int i = 1;i <= m;++i){
std::cin >> u1[i] >> v1[i];
}
for(int i = 1;i <= k;++i){
std::cin >> u2[i] >> v2[i];
mergeG(u2[i],v2[i]);
}
int ans = 0;
for(int i = 1;i <= m;++i){
if(findG(u1[i]) != findG(v1[i])){
++ans;
}else{
mergeF(u1[i],v1[i]);
}
}
for(int i = 1;i <= k;++i){
if(findF(u2[i]) != findF(v2[i])){
mergeF(u2[i],v2[i]);
++ans;
}
}
std::cout << ans << '\n';
}
int main(){
int T;
std::cin >> T;
while(T--) solve();
return 0;
}
T6:(我最爱组合题了!)
题意:
求有多少数组\(a\),满足\(1\le |a| \le n,1 \le a_{i} \le k\),以及\(\prod_{i = 1}^{|a|}a_{i} = x\),其中\((1 \le x \le k)\)
思路:
注意到原题面给出的\(max(k)\)只有\(10^5\),所以他的质因子是有限的,最多只有\(log_{2}k\)个地方不填\(1\),但\(max(n)\)给到了\(9 \times 10^8\)。
那么好,我们就可以试着去计算序列乘积大小为\(i\)长度为\(j\)且序列中每个元素都大于\(1\)的方案数,记为\(f_{i,j}\)。
所以其递推式也是显然的:
其组合意义为在乘积大小为\(\frac{i}{p}\)长度为\(j - 1\)的序列后面加上p这个因子。
有了\(f\)后,那么记\(ans_{k}\)为乘积大小为\(k\)的满足条件的数组个数,那么如果我们有\(j\)个不为1的数,数组长度为\(i\)那么有\(\dbinom{i}{j}\)种填法,则:
又注意到
这一常用恒等式
所以:
实现过程中的小提示:
我们发现,预处理\(f\)数组时的时间复杂度是$$O(K\sqrt{K}logK)$$的,其实很极限,常数一旦稍微大一点,就会TLE大样例,但其实在很早的时候我们是学过\(logK\)枚举因子的(具体实现看代码),所以复杂度就优化为了$$O(Klog^2K)$$
其次,由于\(n\)实在过大,我们不能直接逆元处理组合数,但你又发现\(logK\)又十分小,所以可以每次一接近常数复杂度的方法去求每一次所要的组合数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define int long long
const int K = 1e5 + 10;
const int ML = 20;
const int MOD = 998244353;
int dp[K][ML];
int qpow(int a,int b){
int res = 1;
while(b){
if(b & 1) res = (res * a) % MOD;
a = (a * a) % MOD;
b >>= 1;
}
return res;
}
int inv(int n){
return qpow(n,MOD - 2);
}
void solve(){
int n,k;
std::cin >> k >> n;
std::cout << n << ' ';
for(int i = 2;i <= k;++i){
int ans = 0;
int C = (n + 1) * n / 2;
C %= MOD;
for(int j = 1;j <= std::min(16LL,n);++j){
ans += C * dp[i][j];
ans %= MOD;
C *= (n - j);
C %= MOD;
C *= inv(j + 2);
C %= MOD;
}
std::cout << ans << ' ';
}
std::cout << '\n';
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
for(int i = 2;i < K;++i) {
dp[i][1] = 1;
}
for (int j = 2;j <= 16;++j) {
for (int n = 1;n < K;++n) {
for(int k = 2 * n;k < K;k += n)
{
dp[k][j] = (dp[k][j] + dp[n][j - 1]) % MOD;
}
}
}
int T;
std::cin >> T;
while(T--) solve();
return 0;
}
T7:
题意:
不想写了,直接给网址好了
思路:
这是一道还不错的思维题。
有几个性质,自己画图就能证明:

浙公网安备 33010602011771号