链接:Codeforces Round 1000 (Div. 2)
A:Minimal Coprime
大意:
给定一个区间,定义最小互质区间是边界互质,边界内无互质区间。求这个区间最小互质区间个数
思路:
gcd(l, l + 1) = gcd(1, l) = 1,即相邻数组成的区间互质,那么一段区间的互质区间数就为R-L,
还有个一般情况,就是gcd(1,1) = 1,即1自己单独有个最小互质区间
所以对于左边界为1的情况,如果右边界为1结果就是1,右边界大于1为r,那么结果为r - 1,因为虽然[1, 1]属于,但是[1, 2]却因包含[1, 1]而不属于了
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> PII;
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
const int mod = 998244353;
#define pb push_back
#define vi vector<int>
#define vvi vector<vector<int>>
#define vii vector<pair<int, int>>
#define ff first
#define ss second
// ++ ~! */+- <<>> <> == &^| &&|| =
void solve()
{
int n, m;cin >> n >> m;
if (n == m && n == 1)cout << 1 << endl;
else cout << m - n << endl;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
/* /\_/\
* (= ._.)
* / > \>
*/
B:Subsequence Update
大意:
给定一个数组的一段区间,可以进行且仅进行一次操作
操作为:选择一个子序列(subsequence, 任意删除原数组中的数构成的序列),然后将该序列中的数反转,比如选取[1 2 3 4 5]。中的 1 3 5, 那么结果为[5 2 3 4 1]
目标为使得区间和最大
思路:
首先对于这个操作,我们目的是使得区间和最大,那就要将区间内较小的数换成区间,
限制是只能进行一次操作,那就不能直接求整个数组最小值和了
先将区间分成三段,区间内区间左边区间右边,
首先我们发现一个性质就是只能区间中间与其中一边进行操作,要不然选取的数左边会和右边抵消掉,使得最后还是只交换一边的效果
那么就分别将左边小数、右边小数与中间大数对调结果取最小即可。
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> PII;
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
const int mod = 998244353;
#define pb push_back
#define vi vector<int>
#define vvi vector<vector<int>>
#define vii vector<pair<int, int>>
#define ff first
#define ss second
// ++ ~! */+- <<>> <> == &^| &&|| =
void solve()
{
int n;cin >> n;
int l, r;cin >> l >> r;
vi a(n + 1);
for (int i = 1; i <= n; i++)cin >> a[i];
vi ll, rr, mid;
for (int i = 1; i < l; i++)ll.push_back(a[i]);
for (int i = l; i <= r; i++)mid.push_back(a[i]);
for (int i = r + 1; i <= n; i++)rr.push_back(a[i]);
sort(ll.begin(), ll.end());
sort(rr.begin(), rr.end());
sort(mid.begin(), mid.end(), greater<int>());
int num = 0;
for (int i = 0; i < mid.size(); i++)num += mid[i];
int tmp = 0;
for (int i = 0; i < ll.size() && i < mid.size(); i++)
if (ll[i] <= mid[i])tmp += mid[i] - ll[i];
else break;
int ans = tmp;
tmp = 0;
for (int i = 0; i < rr.size() && i < mid.size(); i++)
if (rr[i] <= mid[i])tmp += mid[i] - rr[i];
else break;
ans = max(ans, tmp);
cout << num - ans << endl;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
/* /\_/\
* (= ._.)
* / > \>
*/
C:Remove Exactly Two
大意:
给定一个最小生成数,求删除两个点形成的连通分量的最大数量
思路:
首先对于两个点的树,删完点就没点了,结果为1
对于大于两个点的树,我们当然是找度数最大的点去删除,如果两点不相连,那么结果为两点度数相加 - 1, 如果相连,那么两点都去掉后相连的地方就没有连通分量了,结果为两点度数相加 - 2.
那么我的思路是第一个度数最大的点跟其他点删除和度数第二大的点跟其他点删除,结果取最大即可。这样可以排除一些问题,比如前三个点度数是相同的,但是第一个点连着后面两个点,那么第三个点就一定不会连着第二个点,因为要连着就成环了。
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> PII;
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
const int mod = 998244353;
#define pb push_back
#define vi vector<int>
#define vvi vector<vector<int>>
#define vii vector<pair<int, int>>
#define ff first
#define ss second
// ++ ~! */+- <<>> <> == &^| &&|| =
void solve()
{
int n;
cin >> n;
map<PII, bool>mp;
vi cnt(n + 1, 0);
for (int i = 1; i < n; i++)
{
int a, b;cin >> a >> b;
cnt[a] ++;
cnt[b] ++;
mp[{a, b}] = true;
mp[{b, a}] = true;
}
if (n == 2)
{
cout << 0 << endl;
return;
}
vii scnt;
for (int i = 1; i <= n; i++)
scnt.push_back({ cnt[i], i });
sort(scnt.begin(), scnt.end(), greater<PII>());
int ans = 0;
for (int i = 1; i < n; i++)
ans = max(ans, scnt[0].ff + scnt[i].ff - 1 - mp[{scnt[0].ss, scnt[i].ss}]);
for(int i = 2; i < n; i ++)
ans = max(ans, scnt[1].ff + scnt[i].ff - 1 - mp[{scnt[1].ss, scnt[i].ss}]);
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
/* /\_/\
* (= ._.)
* / > \>
*/