首页 > 其他分享 >2023冲刺清北营10

2023冲刺清北营10

时间:2023-05-01 19:44:39浏览次数:32  
标签:10 val int sum max1 pos 清北营 long 2023

关于闲话

如果写学术性闲话确实没有必要。(毕竟几乎每天都在写题解)

但如果写别的内容可能大家也不会有太大的兴趣,因为可以用于聊的兴趣就是二次元了,但是现在机房里真正以二次元为爱好的人并不多。(如果真正写闲话可能会变成补番指南)

T1 九转大肠

考虑进行 dp ,设 \(f_{i,j,k}\) 表示当前以 \(i\) 为根, \(j\) 号节点的 \(dfn\) 序为 \(k\) 的方案数。考虑边 \(u\to v\) 的转移,设 \(g_{u,i,j}\) 表示在 \(u\) 的儿子中(不包含 \(v\) )选择 \(i\) 个子树,子树总大小为 \(j\) 的总方案数,此时有转移:

\[f_{v,i,j}\times g_{u,k,t}\to f_{u,i,j+t+1} \]

发现上述转移与 \(t\) 无关,因此可以预处理 \(\sum_t g_{u,k,t}\) ,转移需要枚举 \(i,j,t\) ,容易发现枚举次数为 \(\sum_{(u,v)}siz_v^2(siz_u-siz_v)\) ,将 \(siz_v\) 视为 \(O(n)\) ,上述式子为 \(n\sum_{(u,v)}siz_v(siz_u-siz_v)\) ,后半部分可以理解为树上 \(2\) 点只在 lca 处贡献复杂度,容易发现复杂度为 \(O(n^3)\) 。

然而每次暴力求解 \(g\) 显然会 TLE ,但容易发现这是一个可撤销背包,因此在全部子树贡献的基础上撤销 \(v\) 子树的贡献即可。

code
#include <cstdio>
#include <vector>
using namespace std;
const int max1 = 500;
const int mod = 998244353;

int n, fac[max1 + 5], in[max1 + 5], out[max1 + 5], dfs_clock;
vector <int> edge[max1 + 5];
int f[max1 + 5][max1 + 5], g[max1 + 5][max1 + 5], h[max1 + 5][max1 + 5], tmp[max1 + 5], sum[max1 + 5];

void Add ( int &x, int y )
{
	x = x + y;
	if ( x >= mod )
		x = x - mod;
	return;
}

void Dfs ( int now )
{
	sum[now] = fac[edge[now].size()];
	in[now] = ++dfs_clock;
	for ( auto v : edge[now] )
	{
		Dfs(v);
		sum[now] = 1LL * sum[now] * sum[v] % mod;
	}
	out[now] = dfs_clock;

	int len = edge[now].size(), lim = out[now] - in[now];
	for ( int i = 0; i <= len; i ++ )
		for ( int k = 0; k <= lim; k ++ )
			h[i][k] = 0;
	h[0][0] = 1;
	for ( auto v : edge[now] )
		for ( int i = len; i >= 0; i -- )
			for ( int k = 0; k <= in[v] - in[now] - 1; k ++ )
				if ( h[i][k] )
					Add(h[i + 1][k + out[v] - in[v] + 1], h[i][k]);

	for ( auto v : edge[now] )
	{
		lim -= out[v] - in[v] + 1;
		
		for ( int i = 0; i <= len; i ++ )
			for ( int k = 0; k <= lim; k ++ )
				if ( h[i][k] )
					Add(h[i + 1][k + out[v] - in[v] + 1], mod - h[i][k]);
		
		for ( int k = 0; k <= lim; k ++ )
		{
			tmp[k] = 0;
			for ( int i = 0; i <= len - 1; i ++ )
				tmp[k] = ( tmp[k] + 1LL * h[i][k] * fac[i] % mod * fac[len - 1 - i] ) % mod;
		}

		int x = 1;
		for ( auto p : edge[now] )
			if ( p != v )
				x = 1LL * x * sum[p] % mod;

		for ( int i = in[v]; i <= out[v]; i ++ )
			for ( int k = 1; k <= out[v] - in[v] + 1; k ++ )
				g[i][k] = 0;
		for ( int w = 0; w <= lim; w ++ )
		{
			if ( !tmp[w] )
				continue;
			for ( int i = in[v]; i <= out[v]; i ++ )
			{
				for ( int k = 1; k <= out[v] - in[v] + 1; k ++ )
				{
					if ( !f[i][k] )
						continue;
					g[i][k + w + 1] = ( g[i][k + w + 1] + 1LL * f[i][k] * tmp[w] % mod * x ) % mod;
				}
			}
		}
		for ( int i = in[v]; i <= out[v]; i ++ )
			for ( int k = 1; k <= out[now] - in[now] + 1; k ++ )
				f[i][k] = g[i][k];

		for ( int i = len; i >= 0; i -- )
			for ( int k = 0; k <= lim; k ++ )
				if ( h[i][k] )
					Add(h[i + 1][k + out[v] - in[v] + 1], h[i][k]);

		lim += out[v] - in[v] + 1;
	}

	f[in[now]][1] = sum[now];
	return;
}

int main ()
{
	freopen("intestine.in", "r", stdin);
	freopen("intestine.out", "w", stdout);

	scanf("%d", &n);
	for ( int i = 2, u; i <= n; i ++ )
		{ scanf("%d", &u); edge[u].push_back(i); }

	fac[0] = 1;
	for ( int i = 1; i <= n; i ++ )
		fac[i] = 1LL * fac[i - 1] * i % mod;

	Dfs(1);
	
	for ( int i = 1; i <= n; i ++, putchar('\n') )
		for ( int k = 1; k <= n; k ++ )
			printf("%d ", f[in[i]][k]);
	
	return 0;
}

T2 原本味道

设左端点为 \(A\) ,右端点为 \(B\) ,发现 \(B\) 在 \(q\) 步后到达的位置很容易求解,因此考虑通过求解 \(AB\) 线段最终的距离来得到 \(A\) 端点的移动次数,考虑构造序列 \(t\) ,初始时序列中所有元素为 \(0\) ,不考虑 \(B\) 对 \(A\) 的限制,如果第 \(i\) 秒 \(B\) 向右移动则 \(t_i+1\to t_i\) ,如果 \(A\) 向右移动则 \(t_i-1\to t_i\) ,此时我们初始化 \(AB\) 距离为 \(d\) ,按照时间依次处理每个 \(t_i\) ,容易发现每次的操作为 \(d=\max(1,d+t_i)\) 。

考虑找到最后一个 \(d=1\) 的位置 \(pos\) ,可以发现 \(\sum_{i=pos+1}^{n}t_i\) 就是 \(AB\) 的距离,可以发现 \(\sum_{i=pos+1}^{n}t_i\) 为 \(t\) 序列的最大后缀和,证明考虑如果最大后缀和满足 \(d>1\) ,但是由于开头满足 \(d=1\) ,因此这个位置一定不是最大后缀和。

容易发现 \(t\) 序列以 \(\operatorname{lcm}(a_1+b_1,a_2+b_2)\) 为周期循环,因此考虑将时间轴按照循环周期分段,设当前询问为 \(x\) ,找到 \(x\) 所对应的周期,容易发现 \(t\) 序列的后缀 \(\max\) 只有简单的三种情况:后缀 \(\max\) 全部位于 \(x\) 所对应的周期,一部分位于 \(x\) 所对应的周期,一部分包含倒数第 \(2\) 个周期的一段后缀,包含 \(x\) 所对应的周期到 \(1\) 所对应的周期的一个后缀的全部部分。可以维护一个周期的前缀和以及一个周期的每个位置的后缀 \(\max\) 快速查询。

然而周期的长度很大,因此考虑合并一段 \(t\) 值完全相同的区间,不难发现有用的状态被压缩为 \(O(a+b)\) 级别,可以通过。

code
#include <cstdio>
#include <algorithm>
using namespace std;
const int max1 = 1e6;
const long long inf = 0x3f3f3f3f3f3f3f3f;
int a1, b1, a2, b2, len1, len2, len, q;
long long lcm;
struct Point
{
	long long R; int x;
	Point () {}
	Point ( long long __R, int __x )
		{ R = __R, x = __x; }
	bool operator < ( const Point &A ) const
		{ return R < A.R; }
}val[max1 * 8 + 5];
long long max_suffix[max1 * 8 + 5], prev_sum[max1 * 8 + 5], sum, maxs;
void Build ()
{
	val[0] = Point(0LL, 0); len = 0;
	int i = 1, j = 1; long long pre = 0;
	while ( i <= len1 + len1 && j <= len2 + len2 )
	{
		long long now1, now2;
		if ( i & 1 )
			now1 = 1LL * ( i >> 1 ) * ( a1 + b1 ) + a1;
		else
			now1 = 1LL * ( i >> 1 ) * ( a1 + b1 );
		if ( j & 1 )
			now2 = 1LL * ( j >> 1 ) * ( a2 + b2 ) + a2;
		else
			now2 = 1LL * ( j >> 1 ) * ( a2 + b2 );
		int p = ( i & 1 ) - ( j & 1 );
		if ( now1 < now2 )
		{
			val[++len] = Point(now1, p);
			pre = now1;
			++i;
		}
		else if ( now1 > now2 )
		{
			val[++len] = Point(now2, p);
			pre = now2;
			++j;
		}
		else
		{
			val[++len] = Point(now1, p);
			pre = now1;
			++i, ++j;
		}
	}
	max_suffix[0] = prev_sum[0] = 0LL;
	for ( int i = 1; i <= len; i ++ )
	{
		max_suffix[i] = max_suffix[i - 1] + ( val[i].R - val[i - 1].R ) * val[i].x;
		max_suffix[i] = max(max_suffix[i], 0LL);
		prev_sum[i] = prev_sum[i - 1] + ( val[i].R - val[i - 1].R ) * val[i].x;
	}
	sum = maxs = 0LL;
	for ( int i = len; i >= 1; i -- )
	{
		sum += ( val[i].R - val[i - 1].R ) * val[i].x;
		maxs = max(maxs, sum);
	}
	return;
}
void Solve ()
{
	long long x, belong, ans;
	scanf("%lld", &x);
	belong = ( x - 1 ) / lcm; x = ( x - 1 ) % lcm + 1;
	int pos = upper_bound(val + 1, val + 1 + len, Point(x, 0)) - val - 1;
	ans = ( x - val[pos].R ) * val[pos + 1].x + max_suffix[pos];
	ans = max(ans, 0LL);
	if ( belong )
	{
		ans = max(ans, prev_sum[pos] + ( x - val[pos].R ) * val[pos + 1].x + maxs);
		ans = max(ans, prev_sum[pos] + ( x - val[pos].R ) * val[pos + 1].x + ( belong - 1 ) * sum + maxs);
	}
	long long count = ( belong * lcm + x - 1 ) / ( a1 + b1 ) * a1;
	if ( ( belong * lcm + x - 1 ) % ( a1 + b1 ) + 1 <= a1 )
		count += ( belong * lcm + x - 1 ) % ( a1 + b1 ) + 1;
	else
		count += a1;
	ans = count - ans;
	printf("%lld\n", ans);
	return;
}
int main ()
{
	freopen("snow.in", "r", stdin);
	freopen("snow.out", "w", stdout);
	scanf("%d%d%d%d%d", &a1, &b1, &a2, &b2, &q);
	lcm = 1LL * ( a1 + b1 ) * ( a2 + b2 ) / __gcd(a1 + b1, a2 + b2);
	len1 = lcm / ( a1 + b1 ); len2 = lcm / ( a2 + b2 );
	Build();
	while ( q -- )
		Solve();
	return 0;
}

T3 顶级厨师

首先有一个很显然的结论:如果某项技能在某个时刻变为 \(0\) ,那么这个时刻前一定不会提升该项技能。根据这个结论,我们可以不考虑一项技能变为 \(0\) 所造成的影响。此时有一种显然的 dp ,设 \(f_{i,j,0/1/2}\) 表示上次选择的技能为 \(0/1/2\) ,剩余两次技能最后依次提升在第 \(i,j\) 天,大力分类讨论进行转移即可。

考虑利用 \(w\) 值域很小的限制,此时存在结论:任意两个相邻的相同的技能间隔的天数不超过 \(O(\sqrt{w})\) ,证明考虑如果存在一种技能间隔的天数超过 \(\sqrt{w}\) ,设这分别为第 \(i, j\) 天,考虑将第 \(\tfrac{i+j}{2}\) 天的颜色替换为当前颜色,容易发现这个点产生的最大代价为 \(w\) ,假设其他颜色满足相邻的相同技能间隔天数不超过 \(O(\sqrt{w})\) ,那么删除原有颜色最多产生 \(w\) 的代价,而此时替换产生的贡献约为 \(\tfrac{(j-i)^2}{2}-\tfrac{(\tfrac{j-i}{2})^2}{2}\times 2\) ,也就是 \(\tfrac{(j-i)^2}{4}\) ,容易发现当 \(j-i\ge O(\sqrt{w})\) 时,贡献大于代价。

code
#include <cstdio>
#include <algorithm>
using namespace std;
const int max1 = 1000, B = 200;
const int inf = 0x3f3f3f3f;
int T, n, a[max1 + 5][3];
int now, f[2][max1 + 5][max1 + 5][3];
void Work ()
{
	scanf("%d", &n);
	for ( int i = 1; i <= n; i ++ )
		for ( int k = 0; k < 3; k ++ )
			scanf("%d", &a[i][k]);
	now = 0;
	f[now][1][1][0] = f[now][1][1][1] = f[now][1][1][2] = 0;
	for ( int i = 1; i <= n; i ++ )
	{
		now ^= 1;
		for ( int j = max(1, i - B); j <= i + 1; j ++ )
			for ( int k = max(1, i - B); k <= i + 1; k ++ )
				f[now][j][k][0] = f[now][j][k][1] = f[now][j][k][2] = -inf;
		for ( int j = max(1, i - 1 - B); j <= i; j ++ )
		{
			for ( int k = max(1, i - 1 - B); k <= i; k ++ )
			{
				int x, y, z, valx, valy, valz;
				x = i - 1, y = j, z = k;
				if ( x == i )
					++x;
				if ( y == i )
					++y;
				if ( z == i )
					++z;
				valx = valy = valz = 0;
				if ( x < i )
					valx = i - x;
				if ( y < i )
					valy = i - y;
				if ( z < i )
					valz = i - z;
				f[now][y][z][0] = max(f[now][y][z][0], f[now ^ 1][j][k][0] + a[i][0] - valy - valz);
				f[now][x][z][1] = max(f[now][x][z][1], f[now ^ 1][j][k][0] - valx + a[i][1] - valz);
				f[now][x][y][2] = max(f[now][x][y][2], f[now ^ 1][j][k][0] - valx - valy + a[i][2]);
				

				x = j, y = i - 1, z = k;
				if ( x == i )
					++x;
				if ( y == i )
					++y;
				if ( z == i )
					++z;
				valx = valy = valz = 0;
				if ( x < i )
					valx = i - x;
				if ( y < i )
					valy = i - y;
				if ( z < i )
					valz = i - z;
				f[now][y][z][0] = max(f[now][y][z][0], f[now ^ 1][j][k][1] + a[i][0] - valy - valz);
				f[now][x][z][1] = max(f[now][x][z][1], f[now ^ 1][j][k][1] - valx + a[i][1] - valz);
				f[now][x][y][2] = max(f[now][x][y][2], f[now ^ 1][j][k][1] - valx - valy + a[i][2]);


				x = j, y = k, z = i - 1;
				if ( x == i )
					++x;
				if ( y == i )
					++y;
				if ( z == i )
					++z;
				valx = valy = valz = 0;
				if ( x < i )
					valx = i - x;
				if ( y < i )
					valy = i - y;
				if ( z < i )
					valz = i - z;
				f[now][y][z][0] = max(f[now][y][z][0], f[now ^ 1][j][k][2] + a[i][0] - valy - valz);
				f[now][x][z][1] = max(f[now][x][z][1], f[now ^ 1][j][k][2] - valx + a[i][1] - valz);
				f[now][x][y][2] = max(f[now][x][y][2], f[now ^ 1][j][k][2] - valx - valy + a[i][2]);
			}
		}
	}

	int ans = -inf;
	for ( int i = max(1, n - B); i <= n + 1; i ++ )
		for ( int j = max(1, n - B); j <= n + 1; j ++ )
			for ( int k = 0; k < 3; k ++ )
				ans = max(ans, f[now][i][j][k]);
	printf("%d\n", ans);
	return;
}
int main ()
{
	freopen("codechef.in", "r", stdin);
	freopen("codechef.out", "w", stdout);
	scanf("%d", &T);
	while ( T -- )
		Work();
	return 0;
}

标签:10,val,int,sum,max1,pos,清北营,long,2023
From: https://www.cnblogs.com/KafuuChinocpp/p/17366889.html

相关文章

  • 牛客 55994 2023牛客五一集训派对day3 D Points Construction Problem
    D-PointsConstructionProblem_2023牛客五一集训派对day3(nowcoder.com)将图上恰好\(n\)个点染成黑色,使得图上相邻的黑白点对数量恰好为\(m\)考虑\(n\)个黑点如果不相邻,则两个点的贡献互不影响考虑相邻的情况,我们把相邻的点连边,则贡献为每一个连通块的贡献的和,我们用......
  • 7-010-(LeetCode- 322) 零钱兑换
    1.题目读题 考查点 2.解法思路 代码逻辑 具体实现113.总结......
  • Codeforces Gym 103439D - LIS Counting(猜结论+状压)
    一道需要一些猜结论技巧的中档题。首先突破口在于排列长度恰好等于不是额外输入的某个数\(k\)而是LDS与LIS的乘积,这显然启示我们去找一些性质。根据dilworth定理,最长反链等于最小链覆盖,故LIS的长度,就是最少需要的递减数列的个数使得每个元素被覆盖至少一次,而每个递减数......
  • 10分钟搞定!C++类中构造函数和析构函数的完全指南
    一、初步认识构造函数1.什么是构造函数?要了解构造函数就要先了解一下,类的6个默认成员函数,如下图:构造函数:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次。通俗一点来......
  • PMP-10-项目经理的通用技能
    项目经理这4个能力是最重要的了:一要有大局观,能够站在凌驾于不同相关方之上的角度,去分析怎么协调项目工作,能达到最好的效果。二要有清晰坚定的目标,坚信自己做的事情一定能成功。三要有换位思考的能力,能够站在别人的角度去思考自己的项目,做出有利于所有人的决策。四要有强大的执......
  • n5105软路由 跑分
    鲁大师新版  ......
  • 沁恒 CH32V208(一): CH32V208WBU6 评估板上手报告和Win10环境配置
    目录沁恒CH32V208(一):CH32V208WBU6评估板上手报告和Win10环境配置CH32V208CH32V208系列是沁恒32位RISC-V中比较新的一个系列,基于青稞RISC-V4C内核,最高144MHz主频,64KBSRAM,128KBFlash,供电电压2.5/3.3V.这个型号的特点:除了特有的硬件堆栈区、快速中断入口,片......
  • 2023.4 做题笔记
    出于一些原因,只有4.21往后的题。LOJ6481VisualPython++考虑贪心。非常容易想到,从左往右扫,每次扫到一个右下角时就匹配一个在它上面但是高度差最小的左上角,如果有多个同一高度的可以不用考虑顺序,因为边界重合的情况是不合法的。对于一种匹配方案,怎么判断它合不合法呢?我们同......
  • JetBrains 公布 WebStorm 2023.2 路线图
    JetBrains已公布了WebStorm2023.2版本的路线图,以便用户可以率先了解到官方的规划以及能够预览一下未来能够用上的新功能。主要聚焦于以下内容:稳定的新 UI。这是此版本中的优先事项之一。CSS嵌套支持。WebStorm2023.2计划将添加对 CSS嵌套功能的支持( WEB-57875 ......
  • PAT Advanced 1004. Counting Leaves
    PATAdvanced1004.CountingLeaves1.ProblemDescription:Afamilyhierarchyisusuallypresentedbyapedigreetree.Yourjobistocountthosefamilymemberswhohavenochild.2.InputSpecification:Eachinputfilecontainsonetestcase.Eachcases......