杭电多校7

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值