B. Independent Feedback Vertex Set
题目大意:三角集合中的每一个点都有对应权值,从三角集合中选出若干点并断去这个点与其他点的连边,使得剩下的点集V1形成一棵树,同时使得取出的点集V2形成独立集,求独立集权值和的最大值
题目给出建图方式:
因为要使剩下的点集成为树,所以图中不能有环,所以对于一个小三角形中,至少选出1个点同时我们要满足选出的点组成独立集(看到这个就想起二分图),所以一个小三角形中至多选出1个点所以每个小三角形中只能选出一个点(通过染色来完成)
选点方式:
黑色为可删点,新加点的两个点都是白色才能染为黑色,枚举123号点哪一个点为黑色就行
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100010;
int t, n;
int wt[N];
int color[N];
vector<pair<int,int>> mp;
int fx(int a, int b, int c) {
int sum = 0;
color[1] = a, color[2] = b, color[3] = c;
for (int i = 1; i <= 3; i++)
if (color[i] == 2)
sum += wt[i];
int cnt = 4;
for (auto q : mp) {
if (color[q.first] == 1 && color[q.second] == 1) {
color[cnt] = 2;
sum += wt[cnt];
}
else
color[cnt] = 1;
cnt++;
}
return sum;
}
void solve() {
mp.clear();
cin >> n;
for (int i = 1; i <= n; i++)
cin >> wt[i];
for (int i = 1; i <= n - 3; i++) {
int a = 0, b = 0;
cin >> a >> b;
mp.push_back({ a,b });
}
int ans = -1;
ans = max(fx(2, 1, 1), fx(1, 2, 1));
ans = max(ans, fx(1, 1, 2));
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);//加快cin读取速度的
cin.tie(0);
cin >> t;
while (t--)
solve();
return 0;
}
C. Counting Stickmen
题目大意:
题目大意:在无向图中寻找这样的火柴人,询问最多能找到几个
思路:找到特征点,3号点度数为4(脖子),4,5号点度数为2(手臂),5号点度数为3(腰)
如此一来我们对所有点进行枚举,度数为4的点作为脖子,然后查看以这个点为脖子能找到多少个火柴人
为了方便计算,我们可以首先对每个点进行预处理,计算出每个点作为身体能提供的个数(度数>=3),每个点作为手臂能提供的个数(度数>=2)
我们可以得到公式:
h=从该点连出去的所有点hand[i]之和
b=从该点连出去所有点body[i]之和
该点贡献=body[i]*cal(h - hand[i]) *(rd[i]-3)
但是除此之外,我们不能在一个腰上选两只手,所以我们要减去body[i]*(rd[i]-3)*(b-body[i])
const int mod = 998244353;
const int N = 5e5 + 10;
int t, n, ans;
int rd[N], hand[N], body[N];
vector<int> mp[N];//只有n-1条边,所以vector,装得下
int cal(int x) {
return x * (x - 1) / 2 % mod;
}
void dfs(int x, int fa) {
int b = 0, h = 0, res = 0;
for (auto y : mp[x]) {
b += body[y], h += hand[y];
if (y == fa) continue;
dfs(y, x);
}
if (rd[x] < 4) return;//不能作为脖子
for (auto y : mp[x]) {
//b-body[y]是除开y这个作为身体节点,其他节点上同时选了两只手的情况
res += (body[y] * ((cal(h - hand[y]) - (b - body[y])) % mod) % mod) * (rd[x] - 3) % mod;
}
ans = (ans + res) % mod;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++)mp[i].clear(), rd[i] = 0, hand[i] = body[i] = 0;
for (int i = 1; i <= n - 1; i++) {
int a = 0, b = 0;
cin >> a >> b;
mp[a].push_back(b), mp[b].push_back(a);
rd[a]++, rd[b]++;
}
for (int i = 1; i <= n; i++) {
hand[i] = rd[i] - 1;
if (rd[i] > 2)body[i] = cal(rd[i] - 1);//身子方案根据腿来匹配
}
ans = 0;
dfs(1, -1);
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);//加快cin读取速度的
cin.tie(0);
cin >> t;
while (t--)
solve();
return 0;
}