namomo week1

Posted XINNNNNNNYU

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了namomo week1相关的知识,希望对你有一定的参考价值。

题单

A - Crystalfly

题意

给你一棵树,每个点上有a[i]个蝴蝶,当你走到u点,与u相邻的v的蝴蝶会在t[v]秒之后被惊动逃走(但是v的儿子不会被惊动),问你最多能抓到几只蝴蝶。

思路

可以注意到t数组的范围是1<= t <= 3,我们跟据t把u的儿子分为三类。第一秒可以选三类中的任意一个,第二秒可以选择继续走第一秒选择的儿子,或者是回到u,但是此时第三秒只能走t=3的那个儿子了(一来一回过去了2秒,t小于3的蝴蝶都被惊动飞走了,而往第一秒选择的儿子走显然不是最优)。
这两类行走方案如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCoH09r9-1670481473128)(/upload/2022/02/image-a2f2948ff89345e8a3e1429c4135acb8.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MEPE6XdI-1670481473129)(/upload/2022/02/image-5b2d036624f841cc9af43222cd206d8b.png)]

我们先不管根结点u抓不抓得到
定义

  • dp[i][0]代表i不选并且所有儿子已经不能再抓了的最大值(就是两种路线取最大值)
  • dp[i][1]代表i选并且所有儿子已经不能再抓了的最大值
  • dp[i][2]代表i不选并且所有儿子都不选且已经不能再抓了的最大值
  1. 第一张图
    • 我们选择第一个儿子,其他儿子都不选,所以dp[u][0] = sumdp[son][0] 加上a最大的一个儿子
    • dp[u][2]相比于此时的dp[u][0]差别在于少加一个a最大的儿子
  2. 第二张图
    • 我们枚举那些可以二次拯救的点(即t=3的点),假设所有点的贡献都是dp[i][0],就是sumdp[son][0],就是dp[u][2],然后拯救的点(第三步走的点)j的a[j]可以加上。对于每个点v变成第一步走的点贡献变化是a[v] + dp[v][2] - dp[v][0],显然我们要选变化最大的点走第一步,但是这个点可能和我们枚举的拯救的点是同一点,所以我们要记录变化前二大的点(判断点是否是同一个点,所以还要记录点的编号)。
    • 由此dp[u][0] = max(dp[u][0],dp[u][2] + a[j] + (c[0].second == j ? c[1].first : c[0].first))

最后dp[u][1] = dp[u][0] + a[u]

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\\n'



typedef double db;
typedef long long ll;
typedef unsigned long long ull;

#define debug(args...)  string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); 

void err(istream_iterator<string> it) 
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) 
	cerr << *it << " = " << a << endl;
	err(++it, args...);

int qmi(int a, int k, int p)int res = 1;while (k)if (k & 1) res = (ll)res * a % p;a = (ll)a * a % p;k >>= 1;return res;
int qpow(int a,int b)int res = 1;while(b)if(b&1) res *= a;b>>=1;a*=a;return res;
int mo(int x,int p)return x = ((x%p)+p)%p;
int gcd(int a,int b)return b?gcd(b,a%b):a;


const int maxn = 1e6+7;
const int mod = 1e9+7;
const double eps = 1e-6;
const int INF = 0x3f3f3f3f3f3f3f3f;
int dx[] = 0,0,1,-1, dy[] = 1,-1,0,0;

int T = 1,N,M,K,Q;
int e[maxn],w[maxn],ne[maxn],h[maxn],idx;

void add(int a, int b) 
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;

void add(int a, int b, int c) 
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;

vector<int> a(maxn),t(maxn);
vector<vector<int>> dp(3,vector<int>(maxn));

void dfs(int u, int fa) 
	
	dp[0][u] = 0;
	int max_son = 0;
	int son_num = 0;
	vector<pair<int, int> > c = make_pair(-1e18,0ll), make_pair(-1e18,0ll),make_pair(-1e18,0ll);
	for (int i = h[u]; ~i; i = ne[i]) 
		int j = e[i];
		if (j == fa) continue;
		son_num++;
		dfs(j, u);
		max_son = max(max_son, a[j]);
		dp[0][u] += dp[0][j];
		c[2] = make_pair(a[j] + dp[2][j] - dp[0][j],j);
		sort(c.begin(),c.end(),[&](pair<int, int> aa, pair<int, int> bb) 
			if (aa.first != bb.first) 
				return aa.first > bb.first;
			 else 
				return aa.second < bb.second;
			
		);
	
	
	int temp = dp[0][u];
	dp[2][u] = dp[0][u];
	dp[0][u] = dp[0][u] + max_son;//找个最大儿子进,其他县摆烂
	
	// if (son_num > 1) 
	for (int i = h[u]; ~i; i = ne[i]) 
		int j = e[i];
		if (j == fa || t[j] != 3) continue;
		//回首套的点
		dp[0][u] = max(dp[0][u], temp + a[j] + (c[0].second == j ? c[1].first : c[0].first));
	
	// 
	
	dp[1][u] = dp[0][u] + a[u];
	

void solve()
	
	cin >> N;
	idx = 0;
	memset(h,-1,sizeof(int)*(N+10));


	for (int i = 1; i <= N; i ++) cin >> a[i];
	for (int i = 1; i <= N; i ++) cin >> t[i];
	
	for (int i = 1; i < N; i ++) 
		int u, v;
		cin >> u >> v;
		add(u, v);
		add(v, u);
	
	
	dfs(1, -1);
	cout << dp[1][1] << endl;



signed main()

	ios::sync_with_stdio(false);cin.tie(0);
	cin >> T;
	for (int i = 1; i <= T; i ++) solve();
    return (0-0); //<3
 

B - Towers

题意

给你一棵树,每个点的权值为a[i],现在要求你给每个点的e[i]赋值,使得每个点都存在两个点u,v,这个点在u,v的简单路径上,且e[u]>=a[i]且e[v]>=a[i]。求所有e[i]的和(e[i] >= 0)

思路

我们首先找到权值最大的点root,以它为根。因为根最大,肯定有两个点的e是a[root]是专门用来满足root的。这样对于除了根以外的所有点i都可以从上述两个点其中一个到根再到i,这样来获得u,v的其中一个。而另一个一定是在i的子树当中(包括i本身)。如果子树的最大值大于等于a[i]了,那么e[i]=0,不需要额外开销;如果子树的最大值小于a[i],我们则需要把最大值补成a[i],开销是a[i]-max。

因为要先知道子树中的最大值,所以我们先递归到叶子结点,在回溯时从下往上处理。

当回溯到根的时候,因为我们一开始是设了两个点的e等于a[root]的,他们应该在root不同的儿子里,要不然就不是简单路径了。所以我们把root的每个儿子的子树的最大值存起来,取前两个大的补成a[root]。

但是root可能只有一个儿子,那么我们只需要把e[root]设为a[root]

代码

#include<bits/stdc++.h>
using namespace std;


#define INF 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl "\\n"


#define error(args...)  string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); 

void err(istream_iterator<string> it) 
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) 
	cerr << *it << " = " << a << endl;
	err(++it, args...);

typedef double db;
typedef long long ll;
typedef unsigned long long ull;

int qmi(int a, int k, int p)int res = 1;while (k)if (k & 1) res = (ll)res * a % p;a = (ll)a * a % p;k >>= 1;return res;
int qpow(int a,int b)int res = 1;while(b)if(b&1) res *= a;b>>=1;a*=a;return res;
int mo(int x,int p)return x = ((x%p)+p)%p;
int gcd(int a,int b)return b?gcd(b,a%b):a;


const int maxn = 1e6+7;
const int mod = 1e9+7;
const double eps = 1e-6;
int dx[] = 0,0,1,-1, dy[] = 1,-1,0,0;

int T = 1,N,M,K,Q;

int e[maxn],w[maxn],ne[maxn],h[maxn],idx;

void add(int a, int b) 
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;

void add(int a, int b, int c) 
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;


int ans = 0;
int root = 1;
vector<int> a(maxn);
int dfs(int u, int fa) 
	int mx = 0;
	vector<int> mmax;
	for (int i = h[u]; ~i; i = ne[i]) 
		int j = e[i];
		if (j == fa) continue;
		int t = dfs(j, u);
		mx = max(mx, t);
		if (u == root) 
			mmax.push_back(-t);
		
	
	if (u == root) 
		sort(mmax.begin(),mmax.end());
		ans += max(0ll,a[root] + mmax[0]);
		if (mmax.size() < 2) 
			ans += a[root];
		
		else ans += max(0ll,a[root] + mmax[1]);
		return 0;
	
	if (mx < a[u]) 
		ans += a[u] - mx;
		mx = a[u];
	
	
	return mx;

void solve()
	cin >> N;
	memset(h,-1,sizeof(int)*(N+10));
	for (int i = 1; i <= N; i ++) cin >> a[i];
		
	for (int i = 1; i < N; i ++) 
		int u, v;
		cin >> u >> v;
		add(u, v);
		add(v, u);
	

	
	for (int i = 2; i <= N; i ++) 
		if (a[i] > a[root]Namomo Spring Camp 2022 Div2 Week1 每日一题

Namomo Cockfight Round 3 ab

Namomo Spring Camp 2022 Div1 XOR Inverse Codeforces Round #673 (Div. 1) C. XOR Inverse 按位贪心模拟/字典树分治

Namomo Spring Camp 2022 Div1 XOR Inverse Codeforces Round #673 (Div. 1) C. XOR Inverse 按位贪心模拟/字典树分治

Namomo Spring Camp 2022 Div1 XOR Inverse Codeforces Round #673 (Div. 1) C. XOR Inverse 按位贪心模拟/字典树分治

Namomo Namomo Cockfight Round 3