首页 > 其他分享 >8.23 模拟赛小记

8.23 模拟赛小记

时间:2023-08-23 23:13:26浏览次数:40  
标签:cnt const min int 8.23 序列 mod 模拟 小记

A.

还是单调队列优化 dp 的板子,类似昨天 C。


B.

洛谷原题指路:P1758 [NOI2009] 管道取珠

感觉是比较有难度的 dp。

题目概述:给你两个只有两种字符组成的序列,每次从一个序列末尾取走一位放入新序列的末尾,最后得到 k 种不同的新序列形态。每种形态有 a[i] 种不同的操作,求 \(\sum_{i = 1}^{n}{a[i]^2}\)。

首先想怎么转换 \({a[i]^2}\)。老师提到了P1004 [NOIP2000 提高组] 方格取数P1006 [NOIP2008 提高组] 传纸条
。这两个题目的扩展就有方格取数取 2 次、3 次、k 次等等。所以如果了解过的话,容易想到将平方转换为两次取到了相同序列的方案数。

设 f[i][j][x][y] 表示取了第一次取第一个序列 i 个、第二个序列 j 个。第二次取第一个序列 x 个、第二个序列 y 个的答案平方和。

因为 i + j = x + y,所以可以直接缩掉一维。

考虑取 a[i + 1]、a[j + 1]、b[x + 1]、b[y + 1]。那么就四种情况:11、12、21、22(大概懂我的意思吧?),然后兑起来进行转移。

但是数组如果用 N^3 显然会炸,且 f[i][j][x] 中影响到 i 的只有 i - 1,所以我们用滚动数组滚掉一维。用 cnt 记当前 i 的奇偶,cnt^1 就可以表示前一位的 i - 1。

#include<bits/stdc++.h>
using namespace std;
const int N = 510;
const int mod = 1024523;
int n, m;
char a[N], b[N];
int f[2][N][N];
int main()
{
	scanf("%d%d%s%s", &n, &m, a + 1, b + 1);
	f[0][0][0] = 1;
	int cnt = 0;
	for(int i = 0; i <= n; i ++, cnt ^= 1)
	{
		for(int j = 0; j <= m; j ++ )
		{
			for(int x = 0; x <= n; x ++ )
			{
				int y = i + j - x;
				int t = f[cnt][j][x];
				if(! t || y < 0 || y > m) continue;
				if(a[i + 1] == a[x + 1]) f[cnt^1][j][x + 1] = (f[cnt^1][j][x + 1] + t) % mod;
                if(a[i + 1] == b[y + 1]) f[cnt^1][j][x] = (t + f[cnt^1][j][x]) % mod;
                if(b[j + 1] == a[x + 1]) f[cnt][j+1][x+1] = (t + f[cnt][j + 1][x + 1]) % mod;
                if(b[j + 1] == b[y + 1]) f[cnt][j+1][x] = (t + f[cnt][j + 1][x]) % mod;
                f[cnt][j][x] = 0;
			}
		}
	}
	printf("%d", f[cnt][m][n]); 
}

太妙了,感觉真的充分发挥人类的智慧捏。


C.

洛谷原题指路:P2885 [USACO07NOV] Telephone Wire G

题目:给出若干棵树的高度,可以进行一种操作:把某棵树增高h,花费为 h * h。操作完成后连线,两棵树间花费为高度差 * 定值 c。求两种花费加和最小值。

赛时认为本题很难,赛后发现真的水。

用 f[i][j] 表示选到了第 i 个树时这棵树高度为 j 的最小花费。

一个暴力 dp 很好想:

\(f[i][j] = min(f[i - 1][k] + c * abs(k - j)) + (h[i] - j)^2\)

然后时间复杂度是 \(O(nh^2)\) 的。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 2;
const int inf = 0x7fffffff;
int n, m;
int a[N];
int f[N][102];
int main()
{
	scanf("%d%d", &n, &m);
	int maxn = 0;
	for(int i = 1; i <= n; i ++ )
	{
		scanf("%d", &a[i]);
		maxn = max(maxn, a[i]);
	}
	for(int i = 1; i <= n; i ++ )
	{
		for(int j = a[i]; j <= maxn; j ++ )
		{
			int t = inf;
			for(int k = a[i - 1]; k <= maxn; k ++ )
			{
				t = min(t, f[i - 1][k] + abs(k - j) * m);
			}
			f[i][j] = (a[i] - j) * (a[i] - j) + t;
		}
	}
	int ans = inf;
	for(int i = a[n]; i <= maxn; i ++ ) ans = min(ans, f[n][i]);
	printf("%d", ans);
}

无奈本题数据太弱,或者是洛谷评测机跑的太快,1e9 竟然过了,而且跑得飞快。但这个复杂度显然不对,我们需要优化一下。

观看上面的式子,思考如何优化求 \(min(f[i - 1][k] + c * abs(k - j))\) 的时间。

为了化简本式,我们分类讨论 k 和 j 的大小关系,然后把式子拆开,动态的求 min。

让枚举 k 的一部分合并到枚举 j 的过程中,这样当 k < j 时我们正序枚举 j 的情况,这样始终能保证 k <= j。反之倒序枚举,同理。

int t = inf;
for(int k = 1; k <= a[i]; k ++ ) t = min(t, f[i - 1][k] - m * k);
for(int j = a[i]; j <= maxn; j ++ )
{
	t = min(t, f[i - 1][j] - m * j);
	f[i][j] = min(f[i][j], t + m * j + (j - a[i]) * (j - a[i]));
}
t = inf;
for(int j = maxn; j >= a[i]; j -- )
{
	t = min(t, f[i - 1][j] + m * j);
	f[i][j] = min(f[i][j], t - m * j + (j - a[i]) * (j - a[i]));
}

把第一个树初始化,以及 f[0][0][0] = 1。

最后的复杂度为 \(O(nh)\)。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 2;
const int inf = 0x7fffffff;
int n, m;
int a[N];
int f[N][102];
int main()
{
	scanf("%d%d", &n, &m);
	int maxn = 0;
	for(int i = 1; i <= n; i ++ )
	{
		scanf("%d", &a[i]);
		maxn = max(maxn, a[i]);
	}
	memset(f, 0x3f, sizeof f);
	for(int i = a[1]; i <= maxn; i ++ ) f[1][i] = (a[1] - i) * (a[1] - i);
	for(int i = 2; i <= n; i ++ )
	{
		int t = inf;
		for(int k = 1; k <= a[i]; k ++ ) t = min(t, f[i - 1][k] - m * k);
		for(int j = a[i]; j <= maxn; j ++ )
		{
			t = min(t, f[i - 1][j] - m * j);
			f[i][j] = min(f[i][j], t + m * j + (j - a[i]) * (j - a[i]));
		}
		t = inf;
		for(int j = maxn; j >= a[i]; j -- )
		{
			t = min(t, f[i - 1][j] + m * j);
			f[i][j] = min(f[i][j], t - m * j + (j - a[i]) * (j - a[i]));
		}
	}
	int ans = inf;
	for(int i = a[n]; i <= maxn; i ++ ) ans = min(ans, f[n][i]);
	printf("%d", ans);
}

D.

据说老师本题是出错了,临时改的数据。但不管怎么说,这题又到我的知识漏洞了。

题目概述:有两列长度为 n 的数字,从第 1 行走到第 n 行,每一行只能走二者之一的格子。将你得到的每一个格子数的平方求和后有多少不同的答案。

思路来自同机房大佬,自己想不出来。stO。

大概用了状压 dp 的思想吧,用 f[i][j] 表示走到第 i 行时 j 这个数字能否被拼出来,然后每次枚举 j,再分别考虑这两个格子的取值是否能拼,若能则将答案转移。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const long long N = 104;
int n,a[N][2];
bool f[N][N * N * N];
ll ans;
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++ ) scanf("%d%d", &a[i][1], &a[i][2]);
	f[1][a[1][1] * a[1][1]] = f[1][a[1][2] * a[1][2]] = 1;
	for(int i = 2; i <= n; i ++ )
	{
		for(int j = 1; j <= i * N * N; j ++ )
		{
			if (j - a[i][1] * a[i][1] > 0) f[i][j] |= f[i - 1][j - a[i][1] * a[i][1]];
			if (j - a[i][2] * a[i][2] > 0) f[i][j] |= f[i - 1][j - a[i][2] * a[i][2]];
		}
	}
	for(int i = 1; i <= n * N * N; i ++ ) if(f[n][i]) ans ++;
	printf("%lld\n", ans);
	return 0;
}

同机房大佬还写的 bitset。这种黑科技目前对我来说还是过于高级了点,不是很会用啊所以就不说怎么写的了。


赛后好生气啊,怎么赛时什么也想出不出来呢,也不算难啊(x

标签:cnt,const,min,int,8.23,序列,mod,模拟,小记
From: https://www.cnblogs.com/Moyyer-suiy/p/17652999.html

相关文章

  • 2023.8.23正式操作的第三天
    今天依旧还在编程练习,理解联想起来有点难度1、P33       函数的答案如下 函数调用描述了三个句子,和题目要求吻合,主要是通过\n来断句来作为对此程序的解读切入口;这一个程序和第四个题目的程序不同点,个人认为是体现在jolly和deny可以作为printf函数的平替,但此......
  • 模拟退火算法代码
    当参加数学建模竞赛时,模拟退火算法是一个常用的解题方法之一。以下是一个简单的模拟退火算法的代码示例,用于解决旅行商问题(TSP):点击查看代码importmathimportrandomdefdistance(point1,point2):#计算两个点之间的欧几里德距离returnmath.sqrt((point1[0]-poi......
  • 2023.8.23
    今天竞赛,三个小时,就看了那道pwn题,主要其他我目前也还没学过。pwn就一道题,关键还不会,看ida看了半天,尝试用栈溢出解决,结果发现只在一个不知道哪里的函数用了几次read,read的大小还普遍是1和2,好像还在另一个地方找到了个read,但是read的大小也够不到savedebp。整个竞赛下来,挫败感倒是......
  • 闲话8.23
    今天爽了一天。上午模拟赛......
  • CSP模拟28
    考废了,无语[CF1681E]LabyrinthAdventures题目链接有点神奇的题;首先可以想到简单dp,设$dp_{i,0|1}$表示在第\(i\)层,从上or右门出的最短路径,显然:\[\begin{cases}dp_{i,0}=\min(dp_{i-1,0}+dis_{0,0},dp_{i-1,1}+dis_{1,0})\\dp_{i,1}=\min(dp_{i-1......
  • 8.23
    护照在第\(i\)个点买一张票,就能在\([L_i,R_i]\)中任意行走,求从每个点出发,最少买几张票能走遍\([1,n]\)?tag:最短路,线段树优化建图。题目的问题是求最少代价,于是我们发现题目很像一个最短路模型:\(i\)向一个虚点\(u_i\)连边权为\(1\)的边,\(u_i\)向\([L_i,R_i]\)连代......
  • 2023.8.23 模拟赛
    A一条蛇,有\(K(K\le6)\)个格子,格子必须连续且不能重叠。在\(n\timesm(n,m\le3000)\)的矩阵中放置,有一些格子是不能放的,问方案数。B一棵树\((n\le50000)\).每次询问\([l1,r1],[l2,r2]\)在\(rt\)为根下两两lca的异或和。先处理以\(rt\)为根的问题,发现\(lca_{......
  • 使用条件变量模拟消费者和生产者
    题目简介生产者和消费者问题是一个经典的多线程同步问题,涉及到一个共享的缓冲区,生产者将数据放入缓冲区,消费者从缓冲区中取出数据。问题的关键是要确保生产者和消费者之间的正确交互,避免生产者在缓冲区满时继续生产,消费者在缓冲区空时继续消费。解决方案使用条件变量是一种常见的解......
  • 闲话 8.23
    闲话8.23起因是Rolling_star在考古IMO时发现了这样一道预选题:给出序列\(\{a_n\}\)满足:\[2^n=\sum_{d|n}{a_d}\]求证:\[n|a_n\]我们先做一遍底幂交换(\(Base\)\(power\)\(exchange\)):\[2^d=\sum_{n|d}a_n\]然后再指数降阶($Exponential$$reduction$):\[\bm{2\tim......
  • 8.23 后记
    T1先应该想到\(n^2\)做法,显然连线有交叉是不优的,所以连线不交叉。T2首先\(x^{p_i}\equivq_i(\operatorname{mod}n)\Rightarrowx^{p_i}\equivq_i(\operatorname{mod}p_i)\)然后根据费马小定理或者从\(x^{p_i-2}\equivx^{-1}(\operatorname{mod}p_i)\)可以推出\(x^{......