题解与反思
反思:做前3个题就很困难,对小白一个不友好,看到几题都是大数的运算,以为全是高精度,还特意去学习了一下,结果高精度乘法是o(n^2)的用不了,这时候才考虑其他的做法,卡了好久。
B 交换数字
贪心的部分一开始就猜到了,就是后边的乘法卡了好久
-
首先我们要考虑怎么交换能够使a * b 最小,猜测一下:a用最小的数字,b用最大的数字,然后用样例测试了一下发现是正确的。
-
官方题解给出了证明:太妙了
由于我们交换任意位置的数字,a+b 的值恒为定值,只会改变 a−b 的值,而我们知道 4ab=(a+b) * (a+b) − (a−b) * (a-b),于是我们最大化 a−b 的值即可最小化 ab 的值。
-
最后一点如何求a * b 的值:我们先求出来a的值,因为最后要取模,所以在求a的值的时候每一步都取模就可以了,b同理。
-
a的值的具体求法:
for(int i = 0; i < n; i ++)
{
LL x = a[i] - '0';
aa = aa * 10 + x;
aa %= mod;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int mod = 998244353;
int main()
{
int n;
string a, b;
cin >> n >> a >> b;
for(int i = 0; i < n; i ++)
{
if(a[i] > b[i]) swap(a[i], b[i]);
}
//cout << a << endl << b;
LL aa = 0, bb = 0;
for(int i = 0; i < n; i ++)
{
LL x = a[i] - '0';
//cout << x << endl;
aa = aa * 10 + x;
aa %= mod;
}
for(int i = 0; i < n; i ++)
{
LL x = b[i] - '0';
//cout << x << endl;
bb = bb * 10 + x;
bb %= mod;
}
LL res = aa % mod * bb % mod % mod;
cout << res << endl;
return 0;
}
C 老虎机
求期望,高中数学问题,好久没做了,有点不习惯卡住了。看到最后答案的输出结果一眼快速幂
-
根据题意分三种情况讨论,然后分子分母分开算即可。
-
分类讨论:n >= 3 时
(1)没有相同的图案:C(n,3) * A(3,3) , 选出三的图案进行全排列
(2)两个相同的图案:
C(n,2) * (A(3,3) / A(2,2)) * C(2,1) = C(n,2) * A(3,3), 选出两个图案之后,我们进行全排列,因为有两个相同的图案我们要除以顺序即A(2,2), 最后我们还要选择这两个图案哪一个出现两次即C(2,1)
(3)三个相同的图案:C(n,1)
(4)总的方案数:n * n * n,每个位置上n个图案都可能出现
(5)n = 1 和 n = 2 的情况很容易得出
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int mod = 998244353;
LL qmi(LL a, LL k, LL p)
{
LL res = 1;
res = res % p;
while(k)
{
if(k & 1) res = res * a % p;
a = a * a % p;
k >>= 1;
}
return res;
}
int main()
{
int t;
cin >> t;
while(t --)
{
LL n, a, b, c;
cin >> n >> a >> b >> c;
if(n >= 3)
{
LL res_n = (n - 1) * (n - 2) % mod * a % mod + 3 * (n - 1) * b % mod + c;
res_n %= mod;
LL res_m = n * n % mod;
LL res = res_n * qmi(res_m, mod - 2, mod) % mod;
cout << res << endl;
}
else if(n == 2)
{
LL res_n = 6 * b + 2 * c;
res_n %= mod;
LL res_m = n * n * n % mod;
//cout << res_n << endl << res_m << endl;
LL res = res_n * qmi(res_m, mod - 2, mod) % mod;
cout << res << endl;
}
else
{
LL res = c;
cout << res << endl;
}
}
return 0;
}
D 幻兽帕鲁
不会模拟过程,看了题解是打表找规律。还有一点没注意到的是重新分组之后是根据下标重新判断奇,偶。
- 模拟一下这个过程:
void dfs(int l, int r)
{
if(l == r) return ;
int mid = (l + r) / 2;
vector<int> ou, ji;
for(int i = l; i <= r; i ++)
{
if((i - l) % 2 == 0) ou.push_back(a[i]);
else ji.push_back(a[i]);
}
//for(auto it : ou) cout << it << ' ';
//cout << endl;
for(int i = l, k = 0; i <= mid; i ++)
{
a[i] = ou[k++];
}
for(int i = mid + 1, k = 0; i <= r; i ++)
{
a[i] = ji[k++];
}
dfs(l, mid);
dfs(mid + 1, r);
}
- 打表发现规律,x二进制展开,之后变成逆序的二进制即为答案。
例如3 --> 011 <--> 110 --> 6
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int N = 2e5 + 10;
int a[N];
void dfs(int l, int r)
{
if(l == r) return ;
int mid = (l + r) / 2;
vector<int> ou, ji;
for(int i = l; i <= r; i ++)
{
if((i - l) % 2 == 0) ou.push_back(a[i]);
else ji.push_back(a[i]);
}
//for(auto it : ou) cout << it << ' ';
//cout << endl;
for(int i = l, k = 0; i <= mid; i ++)
{
a[i] = ou[k++];
}
for(int i = mid + 1, k = 0; i <= r; i ++)
{
a[i] = ji[k++];
}
dfs(l, mid);
dfs(mid + 1, r);
}
int main()
{
int n, m;
cin >> n >> m;
while(m --)
{
LL x, res = 0, k = n - 1;
cin >> x;
while(x)
{
if(x & 1)
{
res += (LL)pow(2, k);
}
k --;
x >>= 1;
}
cout << res << endl;
}
return 0;
}
E 奏绝
实现起来倒是不麻烦,但是能够推出来这个结果还是挺难的
1. 有一点需要注意一下,在取模的结果中经常出现,就是计算过程中有减法出现,这时候我们应该先加上mod,防止变成负数,再取模。res = (res % mod + mod) % mod;
- 显然在计算m次答案时无法进行遍历,所以要预处理一些东西。
用a0[i]和a1[i]表示从1到i中0的个数和1的个数,b0[i]和b1[i]表示从1到i中0的坐标总和与1的坐标总和,sum[i]表示1到i总影响值
更新上述列表是简单的,遍历一边序列即可全部更新完成。
最后是计算答案,对于每一个l, r,答案首先为ans[r] - ans[l - 1]
,不过l前面的数仍然对剩余的答案有影响,所以要减去l前面的0与l至r中的1的坐标的组合,以及l前面的1与l~r中的0的坐标的组合。
直接看代码吧:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int N = 2e5 + 10;
// a: 1 or 0 的个数和
// b: i or 0 的坐标和
LL sum[N], a0[N], a1[N], b0[N], b1[N];
int main()
{
int n, m;
cin >> n >> m;
string s;
cin >> s;
s = " " + s;
for(int i = 1; i <= n; i ++)
{
a0[i] = a0[i - 1];
a1[i] = a1[i - 1];
b0[i] = b0[i - 1];
b1[i] = b1[i - 1];
if(s[i] == '0')
{
a0[i] ++;
b0[i] += i;
}
else
{
a1[i] ++;
b1[i] += i;
}
}
for(int i = 1; i <= n; i ++)
{
//sum[i] = sum[i - 1];
if(s[i] == '0')
{
sum[i] = sum[i - 1] + a1[i - 1] * i - b1[i - 1];
}
else
{
sum[i] = sum[i - 1] + a0[i - 1] * i - b0[i - 1];
}
}
while(m --)
{
int l, r;
cin >> l >> r;
LL res = (sum[r] - sum[l - 1]) % mod - (a0[l - 1] * (b1[r] - b1[l - 1]) - (a1[r] - a1[l - 1]) * b0[l - 1]) % mod
- (a1[l - 1] * (b0[r] - b0[l - 1]) - (a0[r] - a0[l - 1]) * b1[l - 1]) % mod;
res = (res % mod + mod) % mod;
cout << res << endl;
}
return 0;
}