8月4日模拟赛题解

本文详述了四道算法竞赛题目,包括签到题、思维题、图论问题和树状数组应用。T1题通过计算DNA序列的Harmming距离找到最优解;T2题涉及矩阵操作,利用乘法结合律优化计算;T3题通过缩点解决强连通子图问题;T4题在树上求按位与与按位或的乘积之和,采用二进制位运算优化。文章提供了详细的解题思路和代码实现。
摘要由CSDN通过智能技术生成

前言

总体评价:

T 1 \rm T1 T1:签到题,用桶乱♂搞♂

然而机房一堆人爆零

评分:10 \color{Red}\colorbox{White}{评分:10} 10

T 2 \rm T2 T2:思维题,思路清奇

为什么我的 50 50 50 分暴力爆零了???

评分:60 \color{Red}\colorbox{White}{评分:60} 60

T 3 \rm T3 T3:缩点乱搞

评分:30 \color{Red}\colorbox{White}{评分:30} 30

T 4 \rm T4 T4:我 ∗ ∗ ** 你个大 ∗ ∗ **

评分:-100 \color{Red}\colorbox{White}{评分:-100} -100

成绩:

100 + 0 + 100 + 0 , r k 2 100+0+100+0,rk2 100+0+100+0,rk2

T 2 \rm T2 T2 暴力怎么炸了???!!!

T 4 \rm T4 T4 树剖一分都没骗到。


S o l u t i o n \rm Solution Solution

T1

UVA1368 DNA序列 DNA Consensus String

输入 n n n 个长度均为 m m m 的字符串,求一个字符串,该序列能满足到 n n n 个序列的总 H a r m m i n g \rm Harmming Harmming 距离最小,若相等,输出字典序最小的。两个等长字符串的 H a r m m i n g \rm Harmming Harmming 距离等于字符不同的的位置个数。

例如, A C G T \rm ACGT ACGT G C G A \rm GCGA GCGA H a r m m i n g \rm Harmming Harmming 距离为 2 2 2(位置 1 1 1 4 4 4 的字符不相同)。

思路:

对于本题,遍历 i = 1 → m i=1\to m i=1m,开桶记录 26 26 26 个字母在这 n n n 个字符串的第 i i i 位出现的次数,统计出现最多的字母,那么答案的第 i i i 位一定是它,剩下的加入 H a r m m i n g \rm Harmming Harmming 距离。

时间复杂度 O ⁡ ( n m t ) \operatorname{O}(nmt) O(nmt)

C o d e \rm Code Code

char s[55][1005];
int a[30];

int main()
{
	int t, n, m;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &m);
		for (re int i = 1; i <= n; i++)
		{
			scanf("%s", s[i]);
		}
		int ans = 0;
		for (re int i = 0; i < m; i++)
		{
			memset(a, 0, sizeof(a));
			for (re int j = 1; j <= n; j++)
			{
				a[s[j][i] - 'A' + 1]++;
			}
			int res = 0;
			char c;
			for (re int j = 1; j <= 26; j++)
			{
				if (res < a[j]) //记录出现次数最多的
				{
					res = a[j];
					c = j + 'A' - 1;
				}
			}
			ans += n - res; //剩下的计入Harmming距离中
			printf("%c", c);
		}
		printf("\n%d\n", ans);
	}
	return 0;
}

T2

有一个 n n n m m m 列的矩阵。矩阵的第一行数字为 1 , 2 , 3 , ⋯   , m 1,2,3,\cdots,m 1,2,3,,m,第二行数字为 m + 1 , m + 2 , ⋯   , 2 m m+1,m+2,\cdots,2m m+1,m+2,,2m,…第 n n n 行数字为 ( n − 1 ) m + 1 , ( n − 1 ) m + 2 , ⋯   , n m (n-1)m+1,(n-1)m+2,\cdots,nm (n1)m+1,(n1)m+2,,nm

例如,对于 n = 3 , m = 4 n=3,m=4 n=3,m=4

1  2  3  4
5  6  7  8
9  10 11 12

需要支持操作:将其中一行或一列的所有数乘以非负数,最后输出矩阵中所有值的总和 m o d    1 0 9 + 7 \mod 10^9+7 mod109+7

思路:

因为同一列上相邻两个数相差 m m m,所以通过第 1 1 1 行可以求出第 2 2 2 行 ~ 第 n n n 行的和;

根据乘法结合律,若同一个数被乘了多次,则改变顺序后结果仍然相等,所以我们可以先把列的算了,再乘上行的。

l ( x ) l(x) l(x) 为第 x x x 列一共应该乘上的数, h ( x ) h(x) h(x) 为第 x x x 行一共应该乘上的数;

则第 i i i 行乘 l ( i ) l(i) l(i) 后,
1 1 1 行的和为 b = ∑ i = 1 m l ( i ) ⋅ i b=\sum\limits_{i=1}^ml(i)\cdot i b=i=1ml(i)i,第 i i i 行比第 i − 1 i-1 i1 行的和多 a = ∑ i = 1 m l ( i ) ⋅ m a=\sum\limits_{i=1}^ml(i)\cdot m a=i=1ml(i)m,故此时第 i i i 行的和为 b + a ( i − 1 ) b+a(i-1) b+a(i1);再乘上 h ( i ) h(i) h(i),最终第 i i i 行的和就是 [ b + a ( i − 1 ) ] ⋅ h ( i ) [b+a(i-1)]\cdot h(i) [b+a(i1)]h(i)

时间复杂度 O ⁡ ( n ) \operatorname{O}(n) O(n)

Caution:此题数据十分毒瘤!!!不开 l o n g l o n g \rm long long longlong 见祖宗!!!每操作一次就要取模!!!

C o d e \rm Code Code

#define int long long

const int MAXN = 1e6 + 5;
const int MOD = 1e9 + 7;

int l[MAXN], h[MAXN];

signed main()
{
	int n = read(), m = read(), k = read();
	for (re int i = 1; i <= m; i++)
	{
		l[i] = 1;
	}
	for (re int i = 1; i <= n; i++)
	{
		h[i] = 1;
	}
	while (k--)
	{
		char op;
		scanf("%s", &op);
		int x = read(), y = read();
		if (op == 'R')
		{
			h[x] = h[x] * y % MOD;
		}
		else
		{
			l[x] = l[x] * y % MOD;
		}
	}
	int a = 0, b = 0, ans = 0;
	for (re int i = 1; i <= m; i++)
	{
		a = (a + l[i] * m % MOD) % MOD;
		b = (b + l[i] * i % MOD) % MOD;
	}
	for (re int i = 1; i <= n; i++)
	{
		ans = (ans + (b + a * (i - 1) % MOD) * h[i] % MOD) % MOD; //最后将每一行的和累加
	}
	write(ans);
	return 0;
}

T3

P2341 [USACO03FALL][HAOI2006]受欢迎的牛 G

给定一张有向图,求有多少个节点,所有节点都可到达它。

思路:

首先,对于一个强连通子图内,若其中一个节点满足要求,则该 S C C \rm SCC SCC 内所有节点都满足要求,这是根据强连通子图的定义。当然,强连通子图满足,则 S C C \rm SCC SCC 也一定满足。

所以直接搞缩点,建新图的同时记录出度,那么答案一定就是出度为 0 0 0 的点所对应的 S C C \rm SCC SCC 中的所有节点,证明如下:

假设有一个点 u u u 出度 ≥ 1 \ge1 1 且所有点都可到达它,则以 u u u 为起点的某一条有向边 < u , v > <u,v> <u,v> v v v 也要能到达 u u u,则 u u u v v v 能合并成一个更大的强连通子图,与 u u u S C C \rm SCC SCC 矛盾。

还有一个问题,就是当出度为 0 0 0 的点的个数超过 1 1 1 时,说明这 2 2 2 个点不能互相到达,此时答案为 0 0 0

时间复杂度 O ⁡ ( n ) \operatorname{O}(n) O(n)


C o d e \rm Code Code

const int MAXN = 1e4 + 5;
const int MAXM = 5e4 + 5;

int cnt, Time, tot;
int head[MAXN], dfn[MAXN], low[MAXN], c[MAXN], sum[MAXN], out[MAXN];
bool ins[MAXN];
stack<int> sta;

struct edge
{
	int to, nxt;
}e[MAXM];

void add(int u, int v)
{
	e[++cnt] = edge{v, head[u]};
	head[u] = cnt;
}

void tarjan(int u)
{
	dfn[u] = low[u] = ++Time;
	sta.push(u);
	ins[u] = true;
	for (re int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min2(low[u], low[v]);
		}
		else if (ins[u])
		{
			low[u] = min2(low[u], dfn[v]);
		}
	}
	if (dfn[u] == low[u])
	{
		tot++;
		int v = 0;
		while (u != v)
		{
			v = sta.top();
			sta.pop();
			ins[v] = false;
			c[v] = tot;
			sum[tot]++;
		}
	}
}

int main()
{
	int n = read(), m = read();
	for (re int i = 1; i <= m; i++)
	{
		int u = read(), v = read();
		add(u, v);
	}
	for (re int i = 1; i <= n; i++)
	{
		if (!dfn[i])
		{
			tarjan(i);
		}
	}
	for (re int u = 1; u <= n; u++) //缩点
	{
		for (re int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			if (c[u] != c[v])
			{
				out[c[u]]++;
			}
		}
	}
	int cnt = 0, ans = 0;
	for (re int i = 1; i <= tot; i++)
	{
		if (!out[i]) //出度为0
		{
			if (cnt++) //超过1个
			{
				putchar('0');
				return 0;
			}
			ans = sum[i];
		}
	}
	write(ans);
	return 0;
}

T4

有一棵 n n n 个点的树,每条边有边权,定义 f ( i , j ) f(i,j) f(i,j) 为点 i i i 到点 j j j 的路径上所有边的边权的按位与( a n d \rm and and 值), g ( i , j ) g(i,j) g(i,j) 为点 i i i 到点 j j j 的路径上所有边的边权的按位或( o r \rm or or 值),求 ∑ i = 1 n ∑ j = i + 1 n f ( i , j ) × g ( i , j ) \sum\limits_{i=1}^n\sum\limits_{j=i+1}^n f(i,j)\times g(i,j) i=1nj=i+1nf(i,j)×g(i,j) 的值。

思路

对于 20 % 20\% 20% 的数据:

  • 直接暴力即可。

对于 50 % 50\% 50% 的数据:

  • d f s \rm dfs dfs 时递推,假设点 u u u 与点 f a ( u ) fa(u) fa(u) 直接的边权为 w ( x ) w(x) w(x) d e p ( u ) ≥ d e p ( v ) dep(u)\ge dep(v) dep(u)dep(v),则 f ( u , v ) = f ( f a ( u ) , v )   and   w ( x ) , g ( u , v ) = g ( f a ( u ) , v )   or   w ( x ) f(u,v)=f(fa(u),v)\,\text{and}\,w(x),g(u,v)=g(fa(u),v)\,\text{or}\,w(x) f(u,v)=f(fa(u),v)andw(x),g(u,v)=g(fa(u),v)orw(x),然后 O ⁡ ( n 2 ) \operatorname{O}(n^2) O(n2) 枚举 u u u v v v 即可。

对于 100 % 100\% 100% 的数据:

在二进制下:

           	1   0   1
	x      	0   1   1
------------------------------
  			1   0   1
  		1	0   1
	0	0	0
------------------------------
	0	1	1	1	1

根据乘法分配律可以发现,对于第 i + j i+j i+j 位,设 c n t f = cntf= cntf= i i i 位为 1 1 1 f ( i , j ) f(i,j) f(i,j) 数量, c n t g cntg cntg 为第 j j j 位为 1 1 1 g ( i , j ) g(i,j) g(i,j) 数量,则这一位上的值应该是 c n t f × c n t g cntf\times cntg cntf×cntg;又因为是第 i + j i+j i+j 位,所以它的总贡献就是 c n t f × c n t g × 2 i + j cntf\times cntg\times 2^{i+j} cntf×cntg×2i+j

要让 f ( u , v ) f(u,v) f(u,v) 的第 i i i 位为 1 1 1,则这条路径上的所有边权的第 i i i 位都要为 1 1 1,所以我们只保留边权的第 i i i 位为 1 1 1 的边,那么只有在每个连通块内的任意两点 < u , v > <u,v> <u,v> 满足 f ( u , v ) f(u,v) f(u,v) 的第 i i i 位为 1 1 1

对于第 j j j 位为 1 1 1 g ( u , v ) g(u,v) g(u,v) o r \rm or or 运算不好处理为 1 1 1 的情况,但是可以处理为 0 0 0 的情况,容斥原理得:第 i i i 位和第 j j j 为都为 1 1 1 = = = i i i 位为 1 1 1 − - i i i 位为 1 1 1,第 j j j 位为 0 0 0 的。

那么我们只保留第 i i i 位为 1 1 1,第 j j j 位为 0 0 0 的就行了。

时间复杂度 O ⁡ ( 400 n ) \operatorname{O}(400n) O(400n)


Code \text{Code} Code

const int MAXN = 5e4 + 5;
const int MOD = 998244353;

int n;
int fa[MAXN], siz[MAXN], u[MAXN], v[MAXN], w[MAXN];

void init()
{
	for (re int i = 1; i <= n; i++)
	{
		fa[i] = i;
		siz[i] = 1;
	}
}

int find(int x)
{
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
	x = find(x), y = find(y);
	if (x != y)
	{
		fa[y] = x;
		siz[x] += siz[y];
	}
}

signed main()
{
	n = read();
	for (re int i = 1; i < n; i++)
	{
		u[i] = read(), v[i] = read(), w[i] = read();
	}
	int ans = 0;
	for (re int i = 0; i <= 20; i++)
	{
		for (re int j = 0; j <= 20; j++)
		{
			int cnt1 = 0, cnt2 = 0;
			init();
			for (re int k = 1; k < n; k++)
			{
				if (w[k] & (1 << i))
				{
					merge(u[k], v[k]);
				}
			}
			for (re int k = 1; k <= n; k++)
			{
				if (k == find(k))
				{
					cnt1 = (cnt1 + (siz[k] - 1) * siz[k] / 2 % MOD) % MOD;
				}
			}
			init();
			for (re int k = 1; k < n; k++)
			{
				if (w[k] & (1 << i))
				{
					if (!(w[k] & (1 << j))) //第i位为1,第j位为0
					{
						merge(u[k], v[k]);
					}
				}
			}
			for (re int k = 1; k <= n; k++)
			{
				if (k == find(k))
				{
					cnt2 = (cnt2 + (siz[k] - 1) * siz[k] / 2 % MOD) % MOD;
				}
			}
			ans = (ans + (1ll /*写成1会WA*/ << (i + j)) % MOD * (cnt1 - cnt2 + MOD) % MOD) % MOD;
		}
	}
	write(ans);
	return 0;
}
weixin028基于微信小程序小说阅读器设计+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值