“蔚来杯”2022牛客暑假多校训练营部分题解 3

EG. Good red-string

题目大意

  • 给定一个长度为 3 n 3n 3n 的字符串,字符串中仅包含 r e d ? 四种字符。
  • 询问能否将 ? 换成其它三种字符中的任意一种,使得字符串能被划分为若干个互不相交的子序列,使得每个子序列组成的串都是 red
  • n ≤ 1 0 5 n \le 10^5 n105

题解

  • c n t c cnt_c cntc 表示字符 c c c 的出现次数。
  • 则填充 ? 的一种方案合法当且仅当对于任意前缀均有 c n t r ≥ c n r e ≥ c n t d cnt_r \ge cnr_e \ge cnt_d cntrcnrecntd,且对于整个字符串 c n t r = c n t e = c n t d cnt_r = cnt_e = cnt_d cntr=cnte=cntd
  • 可转化对于任意前缀均有 c n t e ≥ c n t d cnt_e \ge cnt_d cntecntd,对于任意后缀均有 c n t e ≥ c n t r cnt_e \ge cnt_r cntecntr,同时限制 c n t r , c n t e , c n t d ≤ n cnt_r, cnt_e, cnt_d \le n cntr,cnte,cntdn,最后将多余的 ? 从左到右贪心地填成 r e d 即可。
  • 这样的好处在于对于前后缀的限制,我们都是在扫描的过程中将 ? 改成 e,显然两者不会相互影响,从而保证了正确性。
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::complex;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
typedef complex<ld> com;
typedef set<int>::iterator it;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 3e5 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data, n, top;
int stk[N], num[3], cnt[3];
char s[N];

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

inline int tag(char ch)
{
	if (ch == 'r')
		return 0;
	if (ch == 'e')
		return 1;
	return 2;
}

int main()
{
	read(T_data);
	while (T_data--)
	{
		scanf("%s", s + 1);
		n = strlen(s + 1);
		cnt[0] = cnt[1] = cnt[2] = 0;
		for (int i = 1; i <= n; ++i)
			if (s[i] != '?')
				++cnt[tag(s[i])];
		if (Max(Max(cnt[0], cnt[1]), cnt[2]) > n / 3)
			puts("No");
		else
		{
			num[0] = n / 3 - cnt[0];
			num[1] = n / 3 - cnt[1];
			num[2] = n / 3 - cnt[2];
			
			bool flag = false;
			cnt[2] = cnt[1] = cnt[0] = top = 0;
			for (int i = 1; i <= n; ++i)
				if (s[i] == '?')
					stk[++top] = i;
				else if (s[i] != 'r')
				{
					++cnt[tag(s[i])];
					if (cnt[2] > cnt[1])
					{
						if (!top)
						{
							flag = true;
							break ;
						}
						else
						{
							s[stk[top--]] = 'e';
							--num[1];
							++cnt[1];
						}
					}
				}
			if (flag)
			{
				puts("No");
				continue ;
			}
			cnt[2] = cnt[0] = cnt[1] = top = 0;
			for (int i = n; i >= 1; --i)
				if (s[i] == '?')
					stk[++top] = i;
				else if (s[i] != 'd')
				{
					++cnt[tag(s[i])];
					if (cnt[0] > cnt[1])
					{
						if (!top)
						{
							flag = true;
							break ;
						}
						else
						{
							s[stk[top--]] = 'e';
							--num[1];
							++cnt[1];
						}
					}
				}
			if (flag)
			{
				puts("No");
				continue ;
			}
			for (int i = 1; i <= n; ++i)
				if (s[i] == '?')
				{
					if (num[0])
						--num[0], s[i] = 'r';
					else if (num[1])
						--num[1], s[i] = 'e';
					else if (num[2])
						--num[2], s[i] = 'd';
					else 
					{
						flag = true;
						break ;
					}
				}
			if (num[0] + num[1] + num[2] > 0 || flag)
			{
				puts("No");
				continue ;
			}
			cnt[0] = cnt[1] = cnt[2] = 0;
			for (int i = 1; i <= n; ++i)
			{
				++cnt[tag(s[i])];
				if (cnt[0] < cnt[1] || cnt[1] < cnt[2])
				{
					flag = true;
					break ;
				}
			}
			if (cnt[0] != cnt[1] || cnt[1] != cnt[2] || flag)
			{
				puts("No");
				continue ;
			}
			else 
				puts("Yes");
		} 
	}
	return 0;
}

EK. Killer Sajin’s Matrix

题目大意

  • 给定 n × m n \times m n×m 的网格,要求填入恰好 k k k 个 1,使得每行每列均有奇数个 1。
  • n , m , k ≤ 1 0 5 n,m,k\le 10^5 n,m,k105

题解

  • 考虑若每行每列 1 的个数是确定的要怎样确定最终的方案。
  • 不难想到贪心地让 1 的个数最多的行去匹配 1 的个数最多的列。
  • 因为我们要让每一行去匹配不同的列,将行和列中 1 的个数尽量平均地分配显然不会更劣,直接贪心即可。
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::complex;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
typedef complex<ld> com;
typedef pair<int, int> pir;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 1e5 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data;
int a[N], b[N];
priority_queue<pir> que;
vector<pir> ans, cur;

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

inline bool calc(int *a, int n, int m, int k)
{
	k -= n;
	for (int i = 1; i <= n; ++i)
		a[i] = 1 + (k / (n << 1)) * 2;
	k %= (n << 1);
	if (k & 1)
		return false;	
	for (int i = 1; i <= (k >> 1); ++i)
		a[i] += 2;
	for (int i = 1; i <= n; ++i)
		if (a[i] > m)
			return false;
	return true;
}

int main()
{
	read(T_data);
	int n, m, k;
	while (T_data--)
	{
		read(n); read(m); read(k);
		ans.clear();
		if (k < Max(n, m) || k > 1ll * n * m)
		{
			puts("No");
			continue ;
		}
		else
		{
			if (!calc(a, n, m, k) || !calc(b, m, n, k))
			{
				puts("No");
				continue ;
			}
			else
			{
				for (int i = 1; i <= m; ++i)
					que.push(std::make_pair(b[i], i));
				bool flag = false;
				for (int i = 1; i <= n; ++i)
				{
					while (a[i])
					{
						if (que.empty())
						{
							flag = true;
							break ;
						}
						pir x = que.top();
						que.pop();
						if (!x.first)
						{
							flag = true;
							break ;
						}
						--x.first;
						ans.push_back(std::make_pair(i, x.second));
						cur.push_back(x);
						--a[i];
		 			}
					if (flag)
					{
						cur.clear();
						break ;
					}
					for (pir x : cur)
						que.push(x);
					cur.clear();
				}	
				if (flag)
					puts("No");
				else
				{
					puts("Yes");
					for (pir x : ans)
						printf("%d %d\n", x.first, x.second);
				}
				while (!que.empty())
					que.pop();
			}
		}
	}
	return 0;
}

10K. You are given a tree…

题目大意

  • 给定一棵 n n n 个点的有边权树,每个点有点权 a i a_i ai
  • 要求选出一个至多 k k k 个点的子集,使得子集内点权两两不同且两两点之间的路径并边权和最大。
  • n ≤ 5000 , 2 ≤ k ≤ 5 , 1 ≤ a i ≤ n n \le 5000, 2 \le k \le 5, 1\le a_i\le n n5000,2k5,1ain

题解

  • 考虑给每种点权随机分配一个 [ 1 , k ] [1,k] [1,k] 的新点权,对新点权做树形状压 DP 求出符合条件的答案。
  • 虽然有可能找不到最优解,但方案一定符合条件。
  • 设最优方案中的点权分别为 a i 1 , a i 2 , … , a i k a_{i_1},a_{i_2},\dots,a_{ik} ai1,ai2,,aik,则单次找到最优解的概率为 k ! k k \frac{k!}{k^k} kkk!,重复做 T T T 次至少能找到一次最优解的概率为 1 − ( 1 − k ! k k ) T 1 - (1 - \frac{k!}{k^k})^T 1(1kkk!)T
  • T = 300 T = 300 T=300 时,找到最优解的概率已经足够大,时间复杂度 O ( T n 3 k ) \mathcal O(Tn3^k) O(Tn3k)
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::complex;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
typedef complex<ld> com;
typedef pair<int, int> pir;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 5e3 + 5;
const int S = 1 << 5;
const ll Maxn = 1e18;
const ll Minn = -1e18;
const int mod = 998244353;
int T_data, n, k, C;
int a[N], col[N];
ll f[N][S][2], g[S], ans = Minn;

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

struct arc
{
	int to, cst;
	arc *nxt;
}p[N << 1], *adj[N], *P = p;

inline void linkArc(int x, int y, int z)
{
	(++P)->nxt = adj[x]; adj[x] = P; P->to = y; P->cst = z;	
	(++P)->nxt = adj[y]; adj[y] = P; P->to = x; P->cst = z; 
}

inline void dfs(int x, int Fa)
{
	f[x][1 << col[a[x]]][0] = f[x][1 << col[a[x]]][1] = 0;
	for (arc *e = adj[x]; e; e = e->nxt)
	{
		int y = e->to;
		if (y == Fa)
			continue ;
		dfs(y, x);
	}
	for (int s = 1; s < C; ++s)
		g[s] = Minn;
	for (arc *e = adj[x]; e; e = e->nxt)
	{
		int y = e->to, z = e->cst;
		if (y == Fa)
			continue ;
		for (int s = C - 1; s >= 1; --s)
			for (int s1 = (s - 1) & s; s1; s1 = (s1 - 1) & s)
			{
				ll tmp = Max(f[y][s ^ s1][0], f[y][s ^ s1][1]);
				if (tmp > Minn)
				{
					if (f[x][s1][1] > Minn)
						CkMax(f[x][s][1], f[x][s1][1] + tmp + z);
					if (f[x][s1][0] > Minn)
						CkMax(f[x][s][0], f[x][s1][0] + tmp + z);
				}
			}
		for (int s = 1; s < C; ++s)
			for (int s1 = (s - 1) & s; s1; s1 = (s1 - 1) & s)
			if (g[s1] > Minn)
			{
				ll tmp = Max(f[y][s ^ s1][0], f[y][s ^ s1][1]);
				if (tmp > Minn)
					CkMax(f[x][s][1], g[s1] + tmp + z);
			}
		for (int s = 1; s < C; ++s)
		{
			ll tmp = Max(f[y][s][0], f[y][s][1]);
			if (tmp > Minn)
			{
				CkMax(g[s], tmp + z);
				CkMax(f[x][s][0], tmp + z);
			}
		}
	}
}

inline void solve()
{
	for (int i = 1; i <= n; ++i)
		if (col[a[i]] == -1)
			col[a[i]] = rand() % k;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j < C; ++j)
			f[i][j][0] = f[i][j][1] = Minn;
	dfs(1, 0);
	for (int i = 1; i <= n; ++i)
		for (int s = 1; s < C; ++s)
			CkMax(ans, f[i][s][1]);
	for (int i = 1; i <= n; ++i)
		col[i] = -1;
}

int main()
{
	srand(time(0));
	read(n); read(k);
	for (int i = 1; i <= n; ++i)
		read(a[i]), col[i] = -1;
	for (int i = 1, u, v, w; i < n; ++i)
	{
		read(u); read(v); read(w);
		linkArc(u, v, w);
	}	
	C = 1 << k; 
	for (int i = 1; i <= 300; ++i)
		solve();
	put(ans), putchar('\n');
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值