困难区题目题解1| 豆包MarsCode AI刷题

76 阅读4分钟

27.游戏英雄升级潜力评估

题解:

这道题目感觉应该放在简单区吧.. 根据题目意思分析可得,选择一个等级最低的英雄跟一个等级不是最低的英雄出来历练,则等级大的那个英雄一定可以到达正无穷级,故本题答案为n-等级最小的英雄个数。 代码:

int solution(int n, std::vector<int> u) {
  // write code here
  return n - count(u.begin(), u.end(), *min_element(u.begin(), u.end()));
}

时间复杂度O(n)

字典序最小的01串

题解:

本题考查点是贪心+双指针。

首先,对于第 ii 位置上的 1,要把它变为 0 的最小代价就是找到这个 1 右边的第一个 0 (假设在第 jj 位置),然后交换,代价是 jij-i 。 为什么题面说的是交换相邻位置,但是这里也可以直接交换不相邻的位置的01呢?因为找到的 iijj 之间的子串一定是 1111...0 的形式,每次交换相邻位置把0换到第一位得到的串是 01111...,而直接交换开头的1和结尾的0,得到的串也是0111...,故两种操作是等价的。 那么可以利用双指针,从左到右遍历遇到的每一个1,找到离他们最近的0,如果当前剩余的操作次数能够交换这一对1和0的位置,则交换,并让操作次数减去这次操作的代价,否则,跳过这个1。继续向后寻找满足条件的可交换的1和0。

string solution(int n, int k, string s) {
	// write code here
	s=' '+s;
	for(int i=1,j=1;i<=n;i++)
	{
		while(i<=n&&s[i]=='0')i++;
		j=max(j,i);
		while(j<=n&&s[j]=='1')j++;
		if(j>n)break;
		if(j-i<=k)
		{
			swap(s[i],s[j]);
			k-=j-i;
		}
	}
	return s.substr(1);
}

时间复杂度O(n).

33.卡牌翻面求和问题

题解:

本题考虑动态规划。要使得最终向上的数字之和能被3整除,也就是说最后的和对3取余为0。 设 dp[i][j]dp[i][j] 表示前 ii 个 数字,最终之和对3取余之后为 jj 的方案数。那么 dp[0][0]=1dp[0][0]=1。 当遍历到第 ii 个位置时,当前位置的数字可以选择 aia_ibib_i,设 aia_i 对3取模的结果为 va, bib_i对3取模的结果为vb,则转移方程为 dp[i][(j+va)%3] += dp[i-1][j] , dp[i][(j+vb)%3] += dp[i-1][j],最后答案为dp[n][0]。由于第i个位置的状态只与第i-1位置的状态有关,故可以用滚动数组优化掉dp数组的第一维。

int solution(int n, std::vector<int> a, std::vector<int> b) {
  using ll = long long;
	vector<ll>dp(3,0);
	dp[0]=1;
	for(int i=0;i<n;i++)
	{
		vector<ll>ndp(3,0);
		for(int j=0;j<3;j++)
		{
			int v=(j+a[i])%3;
			ndp[v]=(ndp[v]+dp[j])%mod;
			v=(j+b[i])%3;
			ndp[v]=(ndp[v]+dp[j])%mod;
		}
		dp=ndp;
	}
	return dp[0];
} 

时间复杂度O(3*n)=O(n)

390.小E的区间异或和

题解:

对每个二进制位单独考虑,则数组在当前二进制位下的数值为一个0,1数组 假设当前时第k位二进制 当第ii个位置为0,第jj个位置为1时,这一对0,1一共在i(nj+1)i*(n-j+1)个子串中出现,故这一对0,1的贡献为 i(nj+1)(1<<(k1))i*(n-j+1)*(1<<(k-1))。 1,0对的统计答案方式类似,故可以对每一位二进制位,设num0为当前位置之前的0的位置下标和,num1位当前位置之前1的位置下标和,若当前位置为1,则给答案加上num0(ni+1)(1<<(k1)),然后num0+=inum0*(n-i+1)*(1<<(k-1)),然后num0+=i,否则给答案加上num1(ni+1)(1<<(k1)),然后num1+=inum1*(n-i+1)*(1<<(k-1)),然后num1+=i。 最后返回答案即可。

const int mod=1e9+7;
int solution(int n, const std::vector<int>& a) {
	using ll = long long;
	ll ans = 0;
	for (int j = 31; j >= 0; j--)
	{
		ll num1 = 0;
		ll num0 = 0;
		for (int i = 0; i < n; i++)
		{
			if (a[i] >> j & 1)
			{
				ans += 1LL * (n - i) * num0 % mod * (1LL << j) % mod;
				ans %= mod;
				num1 += i + 1;
                          num1 %= mod;
			}
			else
			{
				ans += 1LL * (n - i) * num1 % mod * (1LL << j) % mod;
				ans %= mod;
				num0 += i + 1;
                          num0 %= mod;
			}
		}
	}
	return ans;
}

时间复杂度O(n*logA)

小K的区间与值和

题解:

本题与上一题的区别在于这次的运算变成了与运算,那么有贡献的对只能是1和1,故设num表示前缀1的下标和,那么对每个1,答案加上num(ni+1)(1<<(k1)),然后num+=inum*(n-i+1)*(1<<(k-1)),然后num+=i,最后输出答案即可。

using ll = long long;
using ull = unsigned long long;
const int mod = 1e9 + 7;
int solution(int n, vector<int> a) {
	// write code here
	ll ans = 0;
	for (int j = 31; j >= 0; j--)
	{
		ll num = 0;
		for (int i = 0; i < n; i++)
		{
			int v = a[i] >> j & 1;
			if (v == 1)
			{
				ans += num * (1LL << j)%mod * (n - i)%mod;
                          ans%=mod;
				num += i + 1;
                          num%=mod;
                
			}
		}
	}
	return ans;
}

248.小X的区间或值和

题解:

这道题变成了或运算,那么贡献计算变成了,如果当前位置为1,则答案加上(前缀下标和)(1<<(k1))(ni+1)(前缀下标和)* (1<<(k-1))*(n-i+1),如果当前位置为0,则答案加上前缀1的位置下标和1<<(k1))(ni+1)前缀1的位置下标和*(1<<(k-1))*(n-i+1),每个位置统计完答案后,更新对应的位置下标和即可。

using ll = long long;
using ull = unsigned long long;
const int mod = 1e9 + 7;

int solution(int n, vector<int> a) {
	// write code here
	ll ans = 0;
	for (int j = 31; j >= 0; j--)
	{
		ll sum = 0;
		ll sum1 = 0;
		for (int i = 0; i < n; i++)
		{
			if (a[i] >> j & 1)
			{
				sum1 += i + 1;
				ans += sum * (n - i) * (1LL << j);
			}
			else
			{
				ans += sum1 * (n - i) * (1LL << j);
			}
			sum += i + 1;
		}
	}
	return ans;
}

时间复杂度O(n*logA)