20231111
2023/11/11
补昨天vp的906
div2 补题到E1还是挺不容易的
今天vp一场,打了一场,本来想去打周赛玩一下的,结果6点人还在食堂。。。
D - Doremy's Connecting Plan
题意:给定两个数字 n 、c和一个长度为n的数组 ,现有n个孤立点,第 i 个孤立点的权值为
,现需要通过建边将所有点全部连通。在第 i 个点和第 j 个点建边需要满足条件:
(S为 i 所在连通块的点和 j 所在连通块的点的集合)。判断能否建成连通图。
思路:赛时想到:因为两个点直接连边,他们成立的条件一定是不变的。然后两个连通块直接肯定选两个编号最小的连。
想到这里思路还是乱的,所以改做法,我们可以用数学的不等式简单推导得到若i和j可以连边,那个i或者j一定可以和1点连边。那么连边的对象就解决了。考虑顺序,我们要满足sum>=j*c-a[j],那么其实我们按不等式右边从小到大排序就行。因为我们判断的时候就把 比sum小的贡献全加进来,那么一定是 最优的
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define Acode ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 2e6 + 10;
int a[N];
struct node
{
int id, val;
int suf;
} s[N];
bool cmp(node a, node b)
{
return a.suf < b.suf;
}
void solve()
{
int n, c;
cin >> n >> c;
for (int i = 1; i <= n; i++)
{
cin >> s[i].val;
s[i].id = i;
s[i].suf = i * c - s[i].val;
}
sort(s + 1 + 1, s + 1 + n, cmp);
int res = s[1].val;
for (int i = 2; i <= n; i++)
{
if (res >= s[i].suf)
{
res += s[i].val;
}
else
{
cout << "No\n";
return;
}
}
cout << "Yes\n";
}
signed main()
{
Acode;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
E1. Doremy's Drying Plan (Easy Version)
题意:2e5区间,每个区间会+1,任意删两个问最多有几个零。 数组长度也是2e5.
思路:考虑到数组长度也是2e5, k==2 那么就分为两种情况:1、我们删两个区间中1最多的两个区间,这里我们不考虑重叠部分可以删2的情况,这是后面一种情况;2、我们看每个值为2的点,考虑到数组长度也是2e5,所以我们可以遍历数组,当一个点的值为2的时候,我们就记录一下是哪两个区间覆盖了他,因为为2,最多有2e5个区间。然后再加上每个区间可以删除1的个数即可
实现:第一种:我们先做一次原数组前缀和,然后做1的个数的前缀和。然后可以O(1)知道每个区间的1的个数
第二种:我们用类似扫描线的思想,对每个+1,-1的事件排序。当有效的区间个数为2时,意味着这个点就是值为2,那么这个区间组合++,代表这个组合能删除2的个数+1.
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define Acode ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 2e6 + 10;
int a[N], a1[N];
int l[N], r[N];
vector<array<int, 3>> evt;
map<pair<int, int>, int> mp;
void solve()
{
evt.clear();
mp.clear();
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
{
a[i] = a1[i] = 0;
}
for (int i = 1; i <= m; i++)
{
cin >> l[i] >> r[i];
a[l[i]]++;
a[r[i] + 1]--;
evt.push_back({l[i], 1, i});
evt.push_back({r[i] + 1, -1, i});
}
int cnt0 = 0;
for (int i = 1; i <= n; i++)
{
a[i] = a[i - 1] + a[i];
if (!a[i])
cnt0++;
}
for (int i = 1; i <= n; i++)
{
a1[i] = a1[i - 1];
if (a[i] == 1)
a1[i]++;
}
// for (int i = 1; i <= n; i++)
// {
// cerr << a[i] << " ";
// }
// cerr << endl;
int res = 0;
vector<int> alls;
for (int i = 1; i <= m; i++)
{
int x = a1[r[i]] - a1[l[i] - 1];
alls.push_back(x);
// cerr << x << endl;
}
sort(alls.begin(), alls.end());
sort(evt.begin(), evt.end());
int ans = cnt0 + alls[m - 1] + alls[m - 2];
set<int> st;
int pos = 0;
for (int i = 1; i <= n; i++)
{
while (pos < evt.size() && evt[pos][0] == i)
{
if (evt[pos][1] == 1)
{
st.insert(evt[pos][2]);
}
else
{
st.erase(evt[pos][2]);
}
pos++;
}
// cerr << st.size() << endl;
if (st.size() == k)
{
int x = *(st.begin());
int y = *(st.rbegin());
// cerr << x << " " << y << endl;
mp[{x, y}]++;
}
}
for (auto it : mp)
{
int num = it.second;
int x = it.first.first;
int y = it.first.second;
int res1 = a1[r[x]] - a1[l[x] - 1];
int res2 = a1[r[y]] - a1[l[y] - 1];
ans = max(ans, num + res1 + res2 + cnt0);
}
cout << ans << endl;
}
signed main()
{
Acode;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
下午 vp Codeforces Round 907 (Div. 2) solve(3/6)
D题失误了, 做法想的太复杂了,然后C题那个前后缀指针写的有点卡,双指针边界一直调不好(老毛病了),还好wa了一发就过了,B题多组数据,有个数组没清空不太应该
https://www.bilibili.com/video/BV17N4y1U7s6/?vd_source=7b3be65640481106bef731ef741a960f
晚上八点打了(AtCoder Beginner Contest 328)solve(5/7)
这把开局不知道自己是谁,什么错误都犯了。。。B题甚至WA了5次才发现return早了导致数据读不完。
后程打的不错,D题想了一会,突然想到一个挺秒的做法,就一发过了。E题一开始没看到只有8的数据范围,看到后就直接开始想dfs枚举。一开始认为如果是完全图的话会跑超时,但是他只有8。邻接矩阵存图,dfs标记打好就一发过了
https://www.bilibili.com/video/BV1q94y137Y1/?vd_source=7b3be65640481106bef731ef741a960f
今天打了两场,明天要好好补题了

浙公网安备 33010602011771号