A - Subsegment Reverse
题目大意:给出n, l, r要求输出1-n, 其中l-r翻转
分析:直接模拟即可。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2e5 + 10;
int main()
{
int n, l, r;
cin >> n >> l >> r;
for (int i = 1; i <= n; i ++ )
{
if (i == l)
{
for (int j = r; j >= l; j -- )
cout << j << ' ';
i = r;
}
else cout << i << ' ';
}
return 0;
}
B - Nutrients
题目大意:给出一个人的希望能够达到的多个健康值, 给出他吃的n个食物能够提供的多个健康值,问他是否达到所以的健康值要求。
分析:模拟即可
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2e5 + 10;
int a[N], b[N];
int main()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i ++ )
cin >> a[i];
for (int i = 0; i < n; i ++ )
{
for (int j = 0; j < m; j ++ )
{
int x;
cin >> x;
b[j] += x;
}
}
bool f = 1;
for (int i = 0; i < m; i ++ )
if (b[i] < a[i]) f = 0;
if (f) cout << "Yes";
else cout << "No";
return 0;
}
C - Keys
题目大意:有N把钥匙,其中有真有假,插入至少K把真钥匙即可开锁。给出一系列插入情况以及对应的开锁情况,求可能的真假钥匙组合有多少种。
分析:可以看到数据非常小,所以可以考虑枚举所有的真假钥匙组合, 组合情况可以用状态压缩来快速处理,再判断所有情况是否符合。
时间复杂度:O(2^n * m * c)
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2e5 + 10;
int c[20];
int a[110][20];
bool f[110];
int main()
{
int n, m, k;
cin >> n >> m >> k;
int res = 0;
for (int i = 0; i < m; i ++ )
{
cin >> c[i];
for (int j = 0; j < c[i]; j ++ )
cin >> a[i][j];
char op;
cin >> op;
if (op == 'o') f[i] = 1;
}
for (int i = 0; i < (1 << n); i ++ )//状态压缩,将i看成二进制数,若j位上为1则代表j这把钥匙为真钥匙,否则为假
{
bool flag = 1;//判断是否满足所有情况
for (int j = 0; j < m && flag; j ++ )
{
int sum = 0;//记录真钥匙数量
for (int v = 0; v < c[j]; v ++ )
if (i >> (a[j][v] - 1) & 1)
sum ++ ;
if (sum >= k && !f[j]) flag = 0;
else if (sum < k && f[j]) flag = 0;
}
if (flag) res ++ ;
}
cout << res;
return 0;
}
D - Masked Popcount
题目大意:给出N, M,求0-N所有数&M后的数转换成二进制总共有多少个1。
分析:我们可以考虑先将M拆分成二进制, 那么二进制位置上为1的才是有效的, 然后枚举这些位置, 判断0-N上有多少数在这些位置上为1,然后全部加起来。而判断有多少数可以先列举几个尝试,通过思考可以发现结果,细节可以看代码。
时间复杂度:O(logM)
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int mod = 998244353;
int main()
{
ll n, m, res = 0;
cin >> n >> m;
for (int i = 0; i < 60; i ++ )
{
if (m >> i & 1)
{
ll pow1 = (ll)1 << i + 1;
ll pow2 = pow1 >> 1;
ll s = n % pow1;
res = (res + max((ll)0, s - pow2 + 1)) % mod;
res = (res + n / pow1 * pow2) % mod;
}
}
cout << res;
return 0;
}
E - Max/Min
题目大意:给出一个长度为n的数组,求所有的二元组{i, j}满足i<j,的max(a[i], a[j]) / min(a[i], a[j])向下取整的和。
分析:首先容易想到先将数组从大到小排序,然后答案就为从左边开始,除以每一个在它右边的数向下取整的和, 但是由于向下取整,会发现很难处理, 然后我们可以发现a[i]的范围在1e6, 那么我们可以考虑用桶s将数据存起来, 然后考虑时间复杂度,容易联想到nlogn的做法也就是n/1+ n/2 +n/3 +n/4+...+n/n这样,如果不考虑向下取整,那么答案为s[i]*sum(s[k], k为i的倍数*k/i)+s[i]*(s[i]-1)/2(1<=I<=1e6)考虑向下取整,我们可以将不是i的倍数数归到i的倍数里,由于i的倍数所含权值是根据大小,所有可以作桶的后缀和,那么从头加到尾即可,具体见代码。
时间复杂度:O(NlogN)
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 1e6 + 10;
ll a[N];//桶
ll s[N];//桶的后缀和
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i ++ )
{
int x;
cin >> x;
a[x] ++ ;
}
for (int i = N - 2; i; i -- )
s[i] = s[i + 1] + a[i];
ll res = 0;
for (ll i = 1; i < N; i ++ )
{
res += (a[i] - 1) * a[i] / 2;//加上自身除自身的值
res += (s[i] - a[i]) * a[i];//加上除i后等于1的数量乘以i的数量
for (ll j = 2; j * i < N; j ++ )//除i后等于j
res += a[i] * s[i * j];//这里加上的是除i后大于等于j的数量乘以i的数量,然后大于j的会在后面加上
}
cout << res;
return 0;
}
赛时cwa了两发, 错在数组开小了,没注意M是偏大的,改了一次后没注意还要一个跟M相关,有点粗心了,f和g还有半小时,有思路但不多,这场感觉还行