NOIP模拟5

战争

非常J的题,我不会用 bitset 所以连暴力都没打出来,后来请教了Dalao后就用暴力过了
发现k20于是直接暴力枚举每个关键点是否选。
首先没有关键点的岛屿内肯定是随机选一个,于是我们先给每个岛屿随机选一个,然后在后来的搜索中用关键点替换,用bitset来判断每个点是否合法,是否可以选。我们只考虑关键点,如果两个点互相有冲突就连边,如果要加入的点与所有已选的点都有连边就是合法的。对于有关键点的岛屿我们提前选了一个非关键点,所以第一次选这个岛屿的时候要先把非关键点去掉。然而对于cu=cv的子任务会T几个,因为选这种关键点肯定不会使答案更优,所以我们能不选就不选,所以加一个特判如果岛屿内还选了其他非关键点就直接跳过

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<bitset>
using namespace std;

#define Miuna printf ("dai suki...")
#define mizuki signed
#define love (1209 & 322 & 901)

namespace LRH
{
	template <typename Shiodome_miuna> inline void in (Shiodome_miuna &x)
	{
		x = 0; char ch, f = 0;
		while (!isdigit (ch = getchar ())) if (ch == '-') f = 1;
		do
		{
			x = (x << 1) + (x << 3) + (ch ^ 48);
		} while (isdigit (ch = getchar ()));
		if (f) x = -x;
	}
	int stk[40], tp;
	template <typename Shiodome_miuna> inline void ot (Shiodome_miuna x, int f = 2)
	{
		if (x < 0) putchar ('-'), x = -x;
		do
		{
			stk[++ tp] = x % 10;
			x /= 10;
		} while (x);
		while (tp) putchar (stk[tp --] | 48);
		if (f == 1) putchar ('\n');
		if (!f) putchar (' ');
	}
}using namespace LRH;
typedef long long ll;
const int maxn = 1e5 + 10;
int n, m, k, ans, tot, fl = 1;
int c[maxn];
int id[45], p[maxn], u[25], v[25];
bool vis[maxn];
vector <int> g[maxn];
bitset <45> s[45], res;
void dfs (int x, int sum)
{
	if (ans < sum) ans = sum, res = s[0];
	if (sum + tot - x + 1 <= ans) return;
	if (vis[c[id[x]]] && fl) {dfs (x + 1, sum); return;}
	if ((s[0] & s[x]) == s[0])
	{
		s[0][x] = 1;
		if (vis[c[id[x]]]) vis[c[id[x]]] = 0, dfs (x + 1, sum), vis[c[id[x]]] = 1;
		else dfs (x + 1, sum + 1);
		s[0][x] = 0;
	}
	dfs (x + 1, sum);
}
mizuki main ()
{
	freopen ("war.in", "r", stdin);
	freopen ("war.out", "w", stdout);
	in (m), in (n);
	for (int i = 1; i <= n; ++ i) in (c[i]), g[c[i]].push_back (i);
	in (k);
	for (int i = 1; i <= k; ++ i)
	{
		in (u[i]), in (v[i]);
		if (!p[u[i]]) id[++ tot] = u[i], p[u[i]] = tot;
		if (!p[v[i]]) id[++ tot] = v[i], p[v[i]] = tot;
		if (c[u[i]] == c[v[i]]) fl = 0;
	}
	for (int i = 1; i <= m; ++ i) for (auto J : g[i]) if (!p[J])
	{
		vis[i] = 1;
		ans ++;
		break;
	}
	for (int i = 1; i <= tot; ++ i) for (int J = 1; J <= tot; ++ J) if (c[id[i]] != c[id[J]]) s[i][J] = 1;
	for (int i = 1; i <= k; ++ i)
	{
		if (c[u[i]] == c[v[i]]) s[p[u[i]]][p[v[i]]] = s[p[v[i]]][p[u[i]]] = 1;
		else s[p[u[i]]][p[v[i]]] = s[p[v[i]]][p[u[i]]] = 0;
	}
	dfs (1, ans);
	ot (ans, 1);
	for (int i = res._Find_first (); i != 45; i = res._Find_next (i)) ot (id[i], 0), vis[c[id[i]]] = 0;
	for (int i = 1; i <= m; ++ i) if (vis[i]) for (auto J : g[i]) if (!p[J])
	{
		ot (J, 0);
		break;
	}
	return love;
}

肥胖

由于我们要走的路肯定是越大越好,所以我们直接建出一个kruskal重构树,每两个点之间的瓶颈就是他们的lca,于是直接考虑树形dp,我们肯定是先吃完一棵子树再去吃另一棵子树,于是就设fi表示吃完这棵子树最大的初始宽度。
于是就可以转移了,kruskal重构树是一个二叉树,如果当前节点的宽度是w, 左儿子是u, 右儿子是vsu表示u的子树内的糖果的总和,那么就是fx=max(min(wsu,fvsu),min(wsv,fusv)),于是直接搞就彳亍了

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

#define Miuna printf ("dai suki...")
#define mizuki signed
#define love (1209 & 322 & 901)

namespace LRH
{
	template <typename Shiodome_miuna> inline void in (Shiodome_miuna &x)
	{
		x = 0; char ch, f = 0;
		while (!isdigit (ch = getchar ())) if (ch == '-') f = 1;
		do
		{
			x = (x << 1) + (x << 3) + (ch ^ 48);
		} while (isdigit (ch = getchar ()));
		if (f) x = -x;
	}
	int stk[40], tp;
	template <typename Shiodome_miuna> inline void ot (Shiodome_miuna x, int f = 2)
	{
		if (x < 0) putchar ('-'), x = -x;
		do
		{
			stk[++ tp] = x % 10;
			x /= 10;
		} while (x);
		while (tp) putchar (stk[tp --] | 48);
		if (f == 1) putchar ('\n');
		if (!f) putchar (' ');
	}
}using namespace LRH;
typedef long long ll;
const int maxn = 2e5 + 10;
#define int ll
int n, m;
int a[maxn], s[maxn], g[maxn], w[maxn];
struct Shiodome
{
	int x, y, w;
	friend bool operator < (Shiodome a, Shiodome b)
	{
		return a.w > b.w;
	}
}E[maxn];
int fa[maxn], t;
inline int fd (int x)
{
	while (x != fa[x]) x = fa[x] = fa[fa[x]];
	return x;
}
struct miuna 
{
	int v, nt;
}e[maxn << 1];
int tot, head[maxn];
inline void add (int u, int v)
{
	e[++ tot] = {v, head[u]};
	head[u] = tot;
}
void dfs (int x, int f)
{
	s[x] = a[x];
	int p1 = 0, p2 = 0;
	for (int i = head[x]; i; i = e[i].nt)
	{
		int y = e[i].v;
		if (y == f) continue;
		dfs (y, x);
		s[x] += s[y];
		if (!p1) p1 = y;
		else p2 = y;
	}
	if (!w[x]) return;
	g[x] = max (min (w[x] - s[p1], g[p2] - s[p1]), min (w[x] - s[p2], g[p1] - s[p2]));
}
mizuki main ()
{
	freopen ("fat.in", "r", stdin);
	freopen ("fat.out", "w", stdout);
	in (n), in (m);
	for (int i = 1; i <= n; ++ i) in (a[i]);
	for (int i = 1; i <= m; ++ i) in (E[i].x), in (E[i].y), in (E[i].w);
	sort (E + 1, E + 1 + m);
	for (int i = 1; i <= n; ++ i) fa[i] = i;
	t = n;
	for (int i = 1; i <= m; ++ i)
	{
		int fx = fd (E[i].x), fy = fd (E[i].y);
		if (fx != fy)
		{
			t ++;
			fa[t] = t;
			fa[fx] = fa[fy] = t;
			w[t] = E[i].w;
			add (t, fx), add (t, fy);
		}
	}
	memset (g, 127, sizeof (g));
	dfs (t, 0);
	if (g[t] <= 0) puts ("-1");
	else ot (g[t]);
	return love;
}

分摊

这题真的是J得不彳亍,每个节点都是好父亲,一拿到钱都会先给儿子分,于是根据它们的操作我们可以发现就是依次填满需求最小的一个儿子,(现在这个需求指的是发给儿子后还能刚好满足自己的钱数,即sizex),其他儿子也获得相应的钱,于是我们就可以获得暴力思路,每个点想要满足需求,那么他的父亲就需要获得min(sizeu,x)+xx为一个点的需求,u是他的兄弟节点,直接每次往上跳父亲扫兄弟就可以。
然而暴力不能体现一个优秀OIer的基本素养,我们考虑优化暴力,发现如果一个点的需求总是比兄弟节点的size都要大,那么每次计算都是直接加上他的兄弟的size,而且这种情况很容易出现,于是我们就可以考虑倍增,先预处理出来兄弟节点的size的前缀和,并预处理出向上跳2J个父亲一直是最大值该点的需求最小是多少,那么向上跳一个父亲显然是兄弟的最大值,跳2J个就是fi,J=max(fx,J1,ffax,J1,J1sumx,J1),这个sum是向上跳2J个父亲如果一直是最大值会加多少,于是就可以直接倍增计算每个点了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

#define Miuna printf ("dai suki...")
#define mizuki signed
#define love (1209 & 322 & 901)

namespace LRH
{
	template <typename Shiodome_miuna> inline void in (Shiodome_miuna &x)
	{
		x = 0; char ch, f = 0;
		while (!isdigit (ch = getchar ())) if (ch == '-') f = 1;
		do
		{
			x = (x << 1) + (x << 3) + (ch ^ 48);
		} while (isdigit (ch = getchar ()));
		if (f) x = -x;
	}
	int stk[40], tp;
	template <typename Shiodome_miuna> inline void ot (Shiodome_miuna x, int f = 2)
	{
		if (x < 0) putchar ('-'), x = -x;
		do
		{
			stk[++ tp] = x % 10;
			x /= 10;
		} while (x);
		while (tp) putchar (stk[tp --] | 48);
		if (f == 1) putchar ('\n');
		if (!f) putchar (' ');
	}
}using namespace LRH;
typedef long long ll;
const int maxn = 3e5 + 10;
int n, m;
struct miuna 
{
	int v, nt;
}e[maxn];
int tot, head[maxn];
inline void add (int u, int v)
{
	e[++ tot] = {v, head[u]};
	head[u] = tot;
}
vector <ll> vec[maxn], s[maxn];
int fa[maxn][23];
ll sum[maxn][23], f[maxn][23];
ll siz[maxn];
void dfs (int x)
{
	for (int i = 1; i <= 20; ++ i) fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for (int i = head[x]; i; i = e[i].nt)
	{
		int y = e[i].v;
		dfs (y);
		siz[x] += siz[y];
	}
}
void exdfs (int x)
{
	if (x)
	{
		if (vec[fa[x][0]].size () == 1) f[x][0] = 0;
		else 
		{
			int pos = vec[fa[x][0]].size () - 1;
			if (siz[x] == vec[fa[x][0]][pos]) pos --;
			f[x][0] = vec[fa[x][0]][pos];
		}
		sum[x][0] = s[fa[x][0]][s[fa[x][0]].size () - 1] - siz[x];
		for (int i = 1; i <= 20; ++ i)
		{
			sum[x][i] = sum[x][i - 1] + sum[fa[x][i - 1]][i - 1];
			f[x][i] = max (f[x][i - 1], f[fa[x][i - 1]][i - 1] - sum[x][i - 1]);
		}
	}
	for (int i = head[x]; i; i = e[i].nt) exdfs (e[i].v);
}
void work (int x)
{
	ll tmp = siz[x];
	while (x)
	{
		for (int i = 20; i >= 0; -- i)
		{
			if (x && tmp >= f[x][i])
			{
				tmp += sum[x][i];
				x = fa[x][i];
			}
		}
		if (x)
		{
			int pos = upper_bound (vec[fa[x][0]].begin (), vec[fa[x][0]].end (), tmp) - vec[fa[x][0]].begin () - 1;
			ll t = tmp;
			if (pos < 0) tmp += t * vec[fa[x][0]].size () - min (siz[x], t);
			else 
			{
				tmp += t * (s[fa[x][0]].size () - pos - 1);
				tmp += s[fa[x][0]][pos];
				tmp -= min (siz[x], t);
			}
			x = fa[x][0];
		}
	}
	ot (tmp, 1);
}
mizuki main ()
{
	freopen ("share.in", "r", stdin);
	freopen ("share.out", "w", stdout);
	in (n);
	for (int i = 1; i <= n; ++ i) in (fa[i][0]), in (siz[i]), add (fa[i][0], i);
	dfs (0);
	for (int i = 1; i <= n; ++ i) vec[fa[i][0]].push_back (siz[i]), s[fa[i][0]].push_back (0);
	for (int i = 0; i <= n; ++ i)
	{
		if (!vec[i].size ()) continue;
		sort (vec[i].begin (), vec[i].end ());
		s[i][0] = vec[i][0];
		for (int J = 1; J < (int) vec[i].size (); ++ J)
			s[i][J] = s[i][J - 1] + vec[i][J];
	}
	exdfs (0);
	for (int i = 1; i <= n; ++ i) work (i);
	return love;
}

修路

还没有来得及改,然而可学长博客去看

posted @   _Mizuki  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示