“蔚来杯“2022牛客暑期多校训练营5

比赛地址

A

题意

给定平面上 n n n 个点,从原点出发直线前往这些点收集食物,收集完再前往下一个点。

每当离开一个有食物的点后该点食物刷新,每次移动距离严格下降。

问最多收集到多少食物。 n ≤ 2 × 1 0 3 n≤2×10^3 n2×103

思路

每两个点之间建一条边,距离作为边权,按照边权从大到小排序。

f [ i ] f[i] f[i] 作为到达点 i i i 能收集到的最大食物数量。

对于边 ( u , v ) (u,v) (u,v),转移方程 f [ v ] = m a x ( f [ v ] , f [ u ] + 1 ) f[v] = max(f[v],f[u]+1) f[v]=max(f[v],f[u]+1)

由于距离要严格下降,因此对于距离相等的边要同时处理。

复杂度 O ( n 2 l o g n 2 + n 2 ) O(n^2logn^2+n^2) O(n2logn2+n2)

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef pair<int,int> PII;
typedef long long ll;
const int N = 2010, INF = 0x3f3f3f3f;
int n;
int f[N], g[N];
struct Edge
{
	int u;
	int v;
	int d;
	bool operator<(const Edge &a)
	{
		return d > a.d;
	}
};
int x[N], y[N];
void solve()
{
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];
	vector<Edge> edge;
	for(int i = 0; i <= n; i++)
		for(int j = 1; j <= n; j++)
			if(i != j) edge.push_back({i, j, (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])});

	sort(edge.begin(), edge.end());
	for(int i = 1; i <= n; i++) f[i] = -INF, g[i] = -INF;
	int m = edge.size();
	f[0] = 0;
	for(int i = 0; i < m;)
	{
		vector<Edge> tmp;
		int j = i;
		while(j < m && edge[j].d == edge[i].d) 
		{
			tmp.push_back(edge[j]);
			j++;
		}
		for(auto t : tmp) g[t.v] = -INF;
		for(auto t : tmp) g[t.v] = max(g[t.v], f[t.u] + 1);
		for(auto t : tmp) f[t.v] = max(f[t.v], g[t.v]);
		i = j;
	}
	int res = 0;
	for(int i = 1; i <= n; i++) res = max(res, f[i]);
	cout << res << endl;
	return;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int T = 1;
//	cin >> T;
	while(T--) solve();
	return 0;
}

D

题意

给定 n n n 个节点的树,树上每个点为 0 , 1 0,1 0,1 两种颜色之一。

问树上连通块满足该连通块构成的生成子图中度为 1 1 1 节点 均同色 的方案数。 n ≤ 3 × 1 0 5 n≤3×10^5 n3×105

思路

均为 0 0 0 和均为 1 1 1 的两种情况解决方法一样,跑两遍即可。

考虑度为 1 1 1 的节点颜色为 f l a g flag flag

d p [ i ] dp[i] dp[i] 为以编号 i i i 的节点为根的子树的方案数。

对于根 u u u 及其子节点 v i v_i vi ,每个子节点 v i v_i vi 都有 d p [ v i ] dp[v_i] dp[vi] 种方案,再加上不选 v i v_i vi 这个子节点这种情况,即为 d p [ v i ] + 1 dp[v_i]+1 dp[vi]+1

对于所有子节点 v i v_i vi ,都可以独立的选和不选,根据乘法原理,得到选择子节点 v i v_i vi 的所有方案数为 ∏ ( d p [ v i ] + 1 ) ∏(dp[v_i]+1) (dp[vi]+1)

还有一种所有子节点都不选,只留下根,但必须满足根为 f l a g flag flag

则有 d p [ u ] = ∏ ( d p [ v i ] + 1 ) − 1 + ( a [ u ] = = f l a g ) dp[u]=∏(dp[v_i]+1)-1+(a[u]==flag) dp[u]=(dp[vi]+1)1+(a[u]==flag)

记录答案时,

a [ u ] = f l a g a[u]=flag a[u]=flag,直接 r e s + = d p [ u ] res+=dp[u] res+=dp[u]

a [ u ] ≠ f l a g a[u]≠flag a[u]=flag,需要减去 u u u 和单独一个子节点 v i v_i vi 形成的树,此时根节点 u u u 的度为1,根据加法原理 r e s + = d p [ u ] − ∑ d p [ v i ] res+=dp[u]-\sum dp[v_i] res+=dp[u]dp[vi]

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef pair<int,int> PII;
typedef long long ll;
const int N = 300010, INF = 0x3f3f3f3f, mod = 1e9 + 7;
int n;
int dp[N];
vector<int> g[N];
int res;
bool a[N];
bool flag;
void dfs(int u, int fa)
{
	int sum = 0, mul = 1;
	for(auto v : g[u])
	{
		if(v == fa) continue;
		dfs(v, u);
		mul = mul * (dp[v] + 1) % mod;
		sum = (sum + dp[v]) % mod;
	}
	dp[u] = mul - 1 + (a[u] == flag);
	if(a[u] == flag) res = (res + dp[u]) % mod;
	else res = (res + dp[u] - sum + mod) % mod;
}
void solve()
{
	cin >> n;
	string s;
	cin >> s;
	for(int i = 0; i < s.length(); i++)
		if(s[i] == '1') a[i + 1] = true;
	for(int i = 1; i <= n - 1; i++)
	{
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	flag = 0;
	dfs(1, 0);
	flag = 1;
	dfs(1, 0);
	cout << res << endl;
	return;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int T = 1;
//	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、付费专栏及课程。

余额充值