AtCoder Beginner Contest 306
B - Base 2
题目大意
给定64个由0和1组成的序列, 如果第i个是1, 结果就加上2的(i-1)次方
解题思路
签到题不多嗦了; 就是注意一点, long long虽然是64位, 但是有一位是符号位, 所以只能取到2^63-1; 而本题最大为2^64-1, 所以要用unsigned long long;
神秘代码
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m;
signed main() {
n = 1;
int res = 0;
for (int i = 1; i <= 64; i++) {
cin >> m;
if (m) res += n;
n = n * 2;
}
cout << res;
return 0;
}
C - Centers
题目大意
给定一个3*n长度的数列, 其中1~n中每个数字都出现了三次, 其中第二次出现的位置就是他们的权值, 把1~n按权值排序;
解题思路
签到题不多嗦了
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m;
vector<int> v[N];
map<int, int> mp;
signed main() {
cin >> n;
for (int i = 1; i <= 3 * n; i++) {
int x;
cin >> x;
v[x].push_back(i);
}
for (int i = 1; i <= n; i++) {
int a = v[i][1];
mp[a] = i;
}
for (auto t : mp) {
cout << t.second << ' ';
}
return 0;
}
D - Poisonous Full-Course
题目大意
小莫去一家特殊的餐馆吃饭, 按顺序给定n到菜, 对于每道菜有两个属性, 有毒or解毒以及他的美味值. 而对于每道菜, 小莫选择吃或者跳过; 如果在中毒状态下又吃到了有毒的食物就会out; 而解毒的食物即使没中毒也可以吃; 问小莫能获得的最大美味值为多少
解题思路
读完题就会发现是一个dp问题, 处理起来也不算复杂; 状态表示f(i, j)表示处理完第j到菜之后, 小莫此时是i状态, i=1则表示中毒, 0表示无毒; 状态计算时每道菜根据有毒or解毒进行分类, 每一类里面小莫都有中毒和无毒两种状态需要计算; 利用吃或不吃来进行状态转移即可;
注意 初始情况下是无毒状态, 故f(1, 0)要初始化为负无穷;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m;
vector<PII> v;
int f[2][N];
signed main() {
cin >> n;
v.push_back({ 0,0 });
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
v.push_back({ a,b });
}
f[1][0] = -1e15;
for (int i = 1; i < v.size(); i++) {
int a = v[i].first;
int b = v[i].second;
if (a == 1) {
f[1][i] = max(f[1][i - 1], f[0][i - 1] + b);
f[0][i] = f[0][i - 1];
}
else {
f[1][i] = f[1][i - 1];
f[0][i] = max(f[0][i - 1], max(f[1][i - 1], f[0][i - 1]) + b);
}
}
cout << max(f[0][n], f[1][n]);
return 0;
}
E - Best Performances
题目大意
给定一个数量为n且初始均为0的数列A; 接着定义了一个数列B和一个函数f(B), B是A降序排列后的结果, f(B)是指数列B前m个数的和; 然后给出了k个操作, 每次操作输入两个数a, b; 意为将A中第a个数替换为b, 对于每次操作输出更新后的f(B);
解题思路
这个题的题意很简单, 主要就是看怎么优化; 因为涉及到排序, 我们可以用两个multiset s1和s2来存储B的前m个数和后(n-m)个数; 用一个数组g来代表A; 一开始别忘了先往s1和s2里插入对应数量的0; 然后对于每次操作, 我们先从g里面取出a位置上原先的数c, 然后对c进行讨论; 如果c在s1中, 那么先删除c, 然后比较b和s2中最大数, 从而判断应该把谁插入到s1中; 如果c在s2中, 那么先删除c, 然后比较b和s1中最小的数, 看是否能把b插入到s1中; 最后别忘了替换g中a位置的数;
注意本题操作的关键是维护s1和s2中元素的个数, 必须一直保持m和(n-m)个;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m, k;
int g[N];
multiset<int, greater<int>> s1;
multiset<int, greater<int>> s2;
signed main() {
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) s1.insert(0);
for (int i = 1; i <= n - m; i++) s2.insert(0);
int res = 0;
for (int i = 1; i <= k; i++) {
int a, b;
cin >> a >> b;
int c = g[a];
if (s1.count(c)) {
s1.erase(s1.find(c));
int m1 = *s2.begin();
if (b >= m1) {
s1.insert(b);
res = res - c + b;
}
else {
s2.erase(s2.begin());
s2.insert(b);
s1.insert(m1);
res = res - c + m1;
}
}
else {
s2.erase(s2.find(c));
int m2 = *s1.rbegin();
if (b<=m2) {
s2.insert(b);
}
else {
s2.insert(m2);
s1.erase(s1.find(m2));
s1.insert(b);
res = res - m2 + b;
}
}
g[a] = b;
cout << res << endl;
}
return 0;
}
F - Merge Sets
题目大意
对于两个数组Si和Sj, 我们定义一个函数f (Si, Sj): 设数组Ci是Si和Sj的并集, 并且按升序排序; 那么f (Si, Sj)的值就是Si的所有元素在Ci中的下标的和, 下标从1开始; 给定n个数组S, 问所有f (Si, Sj)的和, i < j;
解题思路
很明显一一列举i和j合并排序是一定会超时的; 所以我们考虑把所有数组S合并, 这样只需要排一次序就可以了, 并且对于任意的i和j, 在这个大数组中他们元素之间的相对位置是不变的, 所以也是可以进行求解的; 这样我们的求解就需要考虑全局性, 再次之前我们先分析两个数组之间, 对于Si k(数组Si的第k个元素), 它对f (Si, Sj)的贡献就是Sj中所有小于Si k的元素的数量再加上k; 那么对应到大数组中, 它对所有f (Si, Sj(j > i))的贡献就是所有(j > i)的Sj中小于Si k的元素的数量和再加上(n - i)个k; 这就是本题的求解公式, 对于元素的数量和我们可以用树状数组来维护, 总体的复杂度为O(nmlog(nm));
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 1e6 + 10;
typedef pair<int, int> PII;
struct Node{
int id, val;
}f[N];
int n, m, idx;
int t[N], g[N];
bool cmp(Node a, Node b){
return a.val < b.val;
}
int lowbit(int x){
return x & -x;
}
void add (int u, int x){
for(int i = u; i <= n; i += lowbit(i)){
t[i] += x;
}
}
int get(int x){
int res = 0;
for(int i = x; i > 0; i -= lowbit(i)){
res += t[i];
}
return res;
}
signed main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> f[(i - 1) * m + j].val;// 把矩阵压缩为一个数组
f[(i - 1) * m + j].id = i;
}
}
sort(f + 1, f + 1 + n * m, cmp);
int res = 0;
for(int i = 1; i <= n * m; i++){
add(1, 1); // 这两个add是一个类似于差分的操作
add(f[i].id, -1); // 表示对于所有小于j的i, 在它前面都多了一个小于Sik的元素
g[f[i].id]++;
res += get(f[i].id) + g[f[i].id] * (n - f[i].id);
}
cout << res;
return 0;
}