C
题意
给定一个合法的括号序列,如果区间【l, r】之间是一个合法序列,那么就在l, r之间连一条边 问最后有几个连通块
思路
相邻的两个括号并不会使答案+2,只有嵌套的括号才能使答案增加,而嵌套的括号结尾必然是’))'我们只需要维护‘)’的连续出现次数,如果大于1则增加答案
代码实现
void solve()
{
int n;
cin >> n;
string s;
cin >> s;
int res = 0;
int cnt = 0;
for (int i = 0 ; i < s.size(); i ++)
{
if (s[i] == '(')
{
if(cnt)res += cnt - 1;
cnt = 0;
}
else cnt ++;
}
cout << res + cnt << endl;
}
D
题意
给定n个点,m条边 m<= n + 2且给定的图联通无重边 选择一些边为red , 一些边为blue
请问边选定后,能使两个图的得到最小连通块的方案是什么,(打印任意)
思路
根据最小生成树kruskal的推论,一条边可以减少一个连通块,那么我们最好的操作就是求一个最小生成树,那么还有一个小问题,就是剩下的三条边可能是个环,这样答案不是最佳。解决方案是判断剩下的三条边如果是环,我们任意将环上一边变成另一种颜色,在将这边上的某一点的所有边的零一点全变色, 这样能构造出一个没有环的图
代码实现
#include<bits/stdc++.h>
#define x first
#define y second
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 2e5 + 10, mod = 1e9 + 7, INF = 1e9;
PII seg[N];
int p[N];
char ans[N];
int find (int x)
{
if (p[x] != x) p[x] = find (p[x]);
return p[x];
}
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 1 ; i <= m ; i ++)
{
int l, r;
cin >> l >> r;
seg[i] = {l, r};
}
vector<int> red;
vector<vector<pair<int, int>>> g (n + 2);
for (int i = 1 ; i <= n ; i ++) p[i] = i;
for (int i = 1 ; i <= m ; i ++)
{
int l = seg[i].x, r = seg[i].y;
if (find (l) != find (r))
{
ans[i] = '1';
p[find(l)] = find (r);
g[l].push_back ({r, i});
g[r].push_back ({l, i});
}
else ans[i] = '0', red.push_back (i);
}
if (m == n + 2)
{
set<int> S;
for (auto x : red)
S.insert (seg[x].first), S.insert (seg[x].second);
if (S.size() == 3)
{
ans[red[0]] = '1';
int p = seg[red[0]].x;
for (auto [u, id] : g[p]) ans[id] = '0';
}
}
for (int i = 1 ; i <= m ; i ++)
cout << ans[i];
cout << endl;
}
signed main()
{
// freopen("data.in","r",stdin);
// freopen("cfcode.out","w",stdout);
std::ios::sync_with_stdio (false);
std::cin.tie (nullptr);
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}```