3月28日abc补题
C. understory
依旧数据结构爱好者()
用multiset

erase可以删除从a迭代器到b迭代器的数,也就是说这个题可以upper_bound找出迭代器,删除begin到该迭代器的数。
size返回的是一共多少数(也就是有重复的
D Concat Power of 2
观察样例,发现1e9内符合条件的大约是1e6这个数据范围,所以直接暴力,先把1e9内所有的2的幂找出来存进数组里,这是组成我们想要的数的基础。
用一个优先队列小根堆,确保从小到大遍历,每一次从堆顶取出元素然后与2的幂数组进行组合,同时记录队列pop的次数就行了,当cnt=n的时候,就可以输出结束了。
注意 如果用字符串拼接,你将会使用stoll或者to_string,这个时间复杂度是O(n) 的,所以会超时
所以用ksm计算10的幂次方,与前一个数相乘在与后一个数相加。
vs a;
set<int> p;
priority_queue<int, vi, greater<>> q;
void init()
{
int now = 1;
while (now <= 1e9)
{
string str = to_string(now);
q.push(now);
p.insert(now);
a.push_back(str);
now <<= 1;
}
}
void solve()
{
init();
cin >> n;
int cnt = 0;
while (q.size())
{
auto cur = q.top();
q.pop();
cnt++;
if (cnt == n)
{
cout << cur << endl;
return;
}
string str = to_string(cur);
for (auto &i : a)
{
int u = i.size();
int v = stoll(i);
int shu = cur * ksm(10, u, MOD) + v;
if (shu > 1e9 || p.count(shu))
{
continue;
}
q.push(shu);
p.insert(shu);
}
}
}
还有一种表现形式是这样的

E Tree Distance

不难发现,题目要求建一个树,而且这个树上任意两点的最小距离必须符合他给出的值才可以,我们可以用最小生成树生成这个树。
然后对这个树每个点跑最短路,然后看看是否符合标准,如果不符合直接输出No 返回。
处理完每个点如果还没有返回说明这棵树符合条件,那么输出Yes .
// 并查集
int fa[M];
void init()
{
for (int i = 1; i < M; i++)
{
fa[i] = i;
}
}
int find(int a)
{
return a == fa[a] ? a : fa[a] = find(fa[a]);
}
bool same(int a, int b)
{
return find(a) == find(b);
}
void join(int a, int b)
{
a = find(a);
b = find(b);
if (a != b)
{
fa[a] = b;
}
}
struct node
{
int u, v, val;
} a[4500005];
bool cmp(node a, node b)
{
return a.val < b.val;
}
struct op
{
int to, w;
};
vector<op> adj[M];
int dis[3003][3003];
int arr[3003][3003];
void solve()
{
init();
cin >> n;
int cnt = 0;
for (int i = 1; i < n; i++)
{
for (int j = i + 1; j <= n; j++)
{
cin >> a[cnt].val;
arr[i][j] = a[cnt].val;
a[cnt].u = i;
a[cnt].v = j;
cnt++;
}
}
sort(a, a + cnt, cmp);
int num = 0;
for (int i = 0; i <= cnt; i++)
{
if (same(a[i].u, a[i].v))
{
continue;
}
else
{
num++;
join(a[i].u, a[i].v);
adj[a[i].u].push_back({a[i].v, a[i].val});
adj[a[i].v].push_back({a[i].u, a[i].val});
}
}
int ok = 0;
for (int i = 1; i <= n; i++)
{
if (fa[i] == i)
{
ok++;
}
}
if (ok != 1 || num != n - 1)
{
cout << "No" << endl;
return;
}
for (int i = 1; i <= n; i++)
{
vi ans(n + 1, 1e18);
ans[i] = 0;
priority_queue<pii, vector<pii>, greater<>> p;
p.push({0, i});
while (!p.empty())
{
auto [w, v] = p.top();
p.pop();
if (w > ans[v])
{
continue;
}
for (auto [d, j] : adj[v])
{
if (w + j < ans[d])
{
ans[d] = j + w;
p.push({ans[d], d});
}
}
}
for (int j = i + 1; j <= n; j++)
{
if (arr[i][j] != ans[j])
{
cout << "No" << endl;
return;
}
}
}
cout << "Yes" << endl;
}
F Make Bipartite 3

显而易见的二分图,我对二分图的理解就是分左右两边,左边只能连右面,双方内部不能互相连
这个题每个点有黑白两种状态,类似于tWo-set问题,我们可以用扩展域并查集来维护黑白两个信息,1-n为黑,n+1-2n为白,读入两个点,先判断两个点是否已经在一个集合中了,如果是,那么这个图就废了,之后也不可能满足,然后在判断u和v+n是否已经在一个集合中,如果没有才需要进行合并。
如何求出每次询问时的黑色点个数?
可以将黑色点的权值设为1,白色设为0。
那么合并的时候肯定是选两个集合中权值少的作为黑色集合。
这样会有一个问题,那就是每次合并的时候,因为之前的集合也是合并而来,所以如果直接处理新集合肯定不对,所以先对老集合进行处理,分别减去u,v两点所对应的两个黑白集合的权值较小的集合,然后加上较小的新权值。
int fa[M * 2];
int sz[M * 2];
void init()
{
for (int i = 1; i <= n; i++)
{
sz[i] = 1;
}
for (int i = 1; i <= 2 * n; i++)
{
fa[i] = i;
}
}
int find(int a)
{
return a == fa[a] ? a : fa[a] = find(fa[a]);
}
bool same(int a, int b)
{
return find(a) == find(b);
}
void join(int a, int b)
{
a = find(a);
b = find(b);
if (a != b)
{
fa[a] = b;
sz[b] += sz[a];
}
}
void solve()
{
int q;
cin >> n >> q;
int ans = 0;
int ok = 1;
init();
while (q--)
{
int u, v;
cin >> u >> v;
if (!ok)
{
cout << -1 << endl;
continue;
}
if (find(u) == find(v))
{
ok = 0;
cout << -1 << endl;
continue;
}
if (!same(u, v + n))
{
ans -= min(sz[find(u)], sz[find(u + n)]);
ans -= min(sz[find(v)], sz[find(v + n)]);
join(u, v + n);
join(v, u + n);
ans += min(sz[find(u)], sz[find(u + n)]);
}
cout << ans << endl;
}
}

浙公网安备 33010602011771号