A
算法标签: 模拟
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5;
int w[N];
bool check() {
for (int i = 0; i < N; ++i) {
if (w[i] != i + 1) return false;
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
for (int i = 0; i < N; ++i) cin >> w[i];
int i = 0, j = 1;
while (j < N) {
if (w[i] > w[j]) {
swap(w[i], w[j]);
if (check()) {
cout << "Yes" << "\n";
return 0;
}
swap(w[i], w[j]);
}
i++, j++;
}
cout << "No" << "\n";
return 0;
}
B
算法标签: 数学
被坑了, 得开long doublelong \, doublelongdouble精度才够用
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long double LD;
const int N = 110;
const LD EPS = 1e-18;
int n, w[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
if (n <= 2) {
cout << "Yes" << "\n";
return 0;
}
for (int i = 1; i <= n; ++i) cin >> w[i];
int i = 1, j = 2, k = 3;
while (k <= n) {
LD val1 = (LD) w[j] * w[j];
LD val2 = (LD) w[i] * w[k];
if (fabs(val1 - val2) > EPS) {
cout << "No" << "\n";
return 0;
}
i++, j++, k++;
}
cout << "Yes" << "\n";
return 0;
}
C
算法标签: 模拟
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 1010, INF = 0x3f3f3f3f;
int r, c;
char w[N][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> r >> c;
int i1 = INF, j1 = INF, i2 = -INF, j2 = -INF;
for (int i = 1; i <= r; ++i) {
for (int j = 1; j <= c; ++j) {
char c;
cin >> c;
if (c == '#') {
i1 = min(i1, i);
j1 = min(j1, j);
i2 = max(i2, i);
j2 = max(j2, j);
}
w[i][j] = c;
}
}
for (int i = i1; i <= i2; ++i) {
for (int j = j1; j <= j2; ++j) {
if (w[i][j] == '.') {
cout << "No" << "\n";
return 0;
}
}
}
cout << "Yes" << "\n";
return 0;
}
D
算法标签: dfsdfsdfs
思路
异或运算具有交换律, 将nnn个元素进行集合的划分, 但是不考虑顺序, 计算并且去重
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
typedef long long LL;
const int N = 14;
int n;
LL w[N];
unordered_set<LL> s;
vector<LL> cols; // 当前各堆的石子总数
void dfs(int u, LL curr) {
if (u == n) {
s.insert(curr);
return;
}
// 尝试将当前石子放入已有的每一堆
for (int i = 0; i < cols.size(); ++i) {
LL val = curr ^ cols[i];
cols[i] += w[u];
dfs(u + 1, val ^ cols[i]);
cols[i] -= w[u];
}
// 创建新的一堆
cols.push_back(w[u]);
dfs(u + 1, curr ^ w[u]);
cols.pop_back();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0; i < n; ++i) cin >> w[i];
dfs(0, 0);
cout << s.size() << "\n";
return 0;
}
E
算法标签: 二分, 背包问题
思路
求的是摄入最小值最大的可能值, 考虑二分, 设midmidmid是二分出的答案, 那么需要检查当前摄入方案中每种维生素是否都是≥mid\ge mid≥mid的, 可以讲每种食物按照能获取到的维生素进行分类
对于每种维生素食物设置卡路里上限, 假如是X1,X2,X3X_1, X_2, X_3X1,X2,X3, 需要满足X1+X2+X3≤XX_1 + X_2 + X_3 \le XX1+X2+X3≤X, 在这个条件下进行下面的分析
对于每种维生素设置状态表示f[i][j]f[i][j]f[i][j]表示从前iii个食物中选择, 总卡路里不超过jjj的所有方案中能够获取维生素最多的方案, 也就是背包问题, 如果最多获取到的维生素都小于midmidmid, 说明方案一定不合法, 在满足能够获取的最小维生素是midmidmid的情况下, 检查每个维生素对应的食物至少需要多少卡路里, 如果三个卡路里相加大于XXX说明无解返回falsefalsefalse
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 5010, M = 5010, INF = 0x3f3f3f3f;
int n, m;
vector<int> vs[4];
int w[N], c[N];
// f[j]表示总卡路里不超过j的选取方案中维生素含量的最大值
LL f1[M], f2[M], f3[M];
void init() {
for (int i = 0; i < vs[1].size(); ++i) {
int k = vs[1][i];
for (int j = m; j >= c[k]; --j) {
f1[j] = max(f1[j], f1[j - c[k]] + w[k]);
}
}
for (int i = 0; i < vs[2].size(); ++i) {
int k = vs[2][i];
for (int j = m; j >= c[k]; --j) {
f2[j] = max(f2[j], f2[j - c[k]] + w[k]);
}
}
for (int i = 0; i < vs[3].size(); ++i) {
int k = vs[3][i];
for (int j = m; j >= c[k]; --j) {
f3[j] = max(f3[j], f3[j - c[k]] + w[k]);
}
}
}
// 最小维生素含量是mid
bool check(int mid) {
int c1 = INF, c2 = INF, c3 = INF;
for (int i = 0; i <= m; ++i) {
if (f1[i] >= mid) {
c1 = i;
break;
}
}
for (int i = 0; i <= m; ++i) {
if (f2[i] >= mid) {
c2 = i;
break;
}
}
for (int i = 0; i <= m; ++i) {
if (f3[i] >= mid) {
c3 = i;
break;
}
}
LL val = (LL) c1 + c2 + c3 - m;
return val <= 0;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
int v;
cin >> v >> w[i] >> c[i];
vs[v].push_back(i);
}
init();
int l = 0, r = 1e9;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << l << "\n";
return 0;
}
F
算法标签: 容斥原理
思路
f[L,R]f[L, R]f[L,R]的实际含义就是L,RL, RL,R之间有多少个连续的区间
例如上面这个例子, f=3f = 3f=3, 假设对于每个段的起始位置设为xxx, 那么xxx是起始位置等价于xxx出现但是x−1x - 1x−1未出现
因此本质上求区间中xxx出现但是x−1x - 1x−1没出现的数的个数
如何计算每个位置的贡献?
等价于计算在多少个区间[L,R][L, R][L,R]中, AiA_iAi作为某一段的开始位置
区间个数的求和等价于原问题的ansansans
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
int n, w[N];
int l[N], r[N];
// w[i]上一次出现的位置
int vis[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int i = 1; i <= n; ++i) {
l[i] = max(vis[w[i]], vis[w[i] - 1]) + 1;
vis[w[i]] = i;
}
fill(vis, vis + n + 1, n + 1);
for (int i = n; i; --i) {
r[i] = vis[w[i] - 1] - 1;
vis[w[i]] = i;
}
LL ans = 0;
for (int i = 1; i <= n; ++i) {
LL cnt1 = i - l[i] + 1;
LL cnt2 = r[i] - i + 1;
ans += cnt1 * cnt2;
}
cout << ans << "\n";
return 0;
}