造数
思路:我们可以倒着做,看看 n n n是否可以变成 1 1 1,最优的一定是能除 2 2 2就除 2 2 2,当变为奇数就减 1 1 1,最后还剩 2 2 2的时就之间减 2 2 2
void solve() {
int n; cin >> n;
int ans = 0;
while (true) {
if (n == 2 || n == 0) break;
ans ++;
if (n & 1) n --;
else n /= 2;
}
if (n == 2) ans ++;
cout << ans << '\n';
}
爱探险的朵拉
思路:比赛时是真的没搞清楚题目意思,其实就是一个基环树森林,找出经过最多点的基环树,先用拓扑排序将环上的点找出来,再 d f s dfs dfs将每一个环用 m a p < i n t , v e c t o r < i n t > > map<int, vector<int>> map<int,vector<int>>都记录下来,建图的时候反着建,因为要找以环上一个点为起点最长的链,其实也不用建图,直接记录后继节点是谁也可以,然后遍历所有基环树更新答案
int a[N], vis[N], d[N], son[N], vi[N];
vector<int> g[N];
int ans, n, idx, cnt;
map<int, vector<int>> mp;
void topsort() {
queue<int> q;
for (int i = 1; i <= n; i ++) {
if (d[i] == 1) {
q.push(i);
vis[i] = 1;
}
}
while (q.size()) {
auto t = q.front();
q.pop();
if (vis[son[t]]) continue;
d[son[t]] --;
if (d[son[t]] == 1) {
q.push(son[t]);
vis[son[t]] = 1;
}
}
}
void dfs(int u) {
if (vi[u]) return ;
vi[u] = 1;
mp[idx].push_back(u);
dfs(son[u]);
};
void dfs1(int u, int d) {
if (!g[u].size()) {
cnt = max(cnt, d);
}
for (auto v : g[u]) {
if (vi[v]) continue;
dfs1(v, d + 1);
}
};
void solve() {
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> son[i];
g[son[i]].push_back(i);
d[i] ++;
d[son[i]] ++;
}
topsort();
for (int i = 1; i <= n; i ++) {
if (d[i] == 2 && !vi[i]) dfs(i), idx ++;
}
for (auto [x, y] : mp) {
int len = 0;
for (auto j : y) {
cnt = 0;
dfs1(j, 0);
len = max(len, cnt);
}
ans = max(ans, (int)y.size() + len);
}
cout << ans << '\n';
}
有大家喜欢的零食吗
思路:二分图最大匹配模板题,匈牙利算法,没啥好说的
vector<int> g[N];
int match[N], vis[N], ans;
bool dfs(int u) {
for (auto v : g[u]) {
if (vis[v]) continue;
vis[v] = 1;
if (!match[v] || dfs(match[v])) {
match[v] = u;
return 1;
}
}
return 0;
}
void solve() {
int n; cin >> n;
for (int i = 1; i <= n; i ++) {
int k; cin >> k;
for (int j = 1, x; j <= k; j ++) {
cin >> x;
g[i].push_back(x);
}
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) vis[j] = 0;
if (dfs(i)) ans ++;
}
if (ans == n) cout << "Yes" << '\n';
else cout << "No" << '\n' << n - ans << '\n';
}
小蓝的二进制询问
思路:看着这么大的数据范围,应该是要按每一位求贡献,打表发现从最低位开始,每增加一位周期乘 2 2 2, 0 0 0, 1 1 1的数量以及总和都乘 2 2 2,每一位的贡献就是周期数 ∗ * ∗周期的总和 + + +周期余数的和
int get(int s) {
s ++;
int sum = 0, zq = 2, gs = 1, zqs = 1;
for (int j = 0; j < 61; j ++) {
int x = s / zq, y = s % zq;
sum += x * zqs % mod;
sum %= mod;
sum += max((y - gs), 0ll);
sum %= mod;
gs *= 2;
zq *= 2;
zqs *= 2;
}
return sum;
}
void solve() {
int t; cin >> t;
while (t --) {
int l, r; cin >> l >> r;
cout << (get(r) - get(l - 1) + mod) % mod << '\n';
}
}
奇妙的脑回路
太难,以后再补
两难抉择新编
思路:直接暴力求解即可,复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
void solve() {
int n; cin >> n;
vector<int> a(n + 1);
int sum = 0;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
sum ^= a[i];
}
int ans = 0;
for (int i = 1; i <= n; i ++) {
sum ^= a[i];
for (int j = 1; j <= n / i; j ++) {
ans = max(ans, sum ^ (a[i] + j));
ans = max(ans, sum ^ (a[i] * j));
}
sum ^= a[i];
}
cout << ans << '\n';
}
旅途的终点
思路:反悔贪心,先全部把 k k k个神力都用完,放到小根堆里面,当我后面遇见的所消耗的比小根堆里最小的大,我就用小根堆里最小的替换当前的所遇见的
void solve() {
int n, m, k; cin >> n >> m >> k;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
if (k >= n) {
cout << n << '\n';
return ;
}
priority_queue<int, vector<int>, greater<int>> Q;
for (int i = 1; i <= k; i ++) {
Q.push(a[i]);
}
int ans = k;
for (int i = k + 1; i <= n; i ++) {
if (Q.size() && Q.top() < a[i]) {
if (m - Q.top() > 0) {
ans ++;
m -= Q.top();
Q.pop();
Q.push(a[i]);
} else if (m - a[i] > 0) {
ans ++;
m -= a[i];
} else break;
} else {
if (m - a[i] > 0) {
ans ++;
m -= a[i];
} else break;
}
}
cout << ans << '\n';
}
两难抉择
思路:数组里的最大值大于 1 1 1是乘 n n n,否则加 n n n
void solve() {
int n; cin >> n;
vector<int> a(n + 1);
int mx = 0, sum = 0;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
mx = max(mx, a[i]);
sum += a[i];
}
if (mx == 1) {
cout << sum - mx + mx + n << '\n';
} else {
cout << sum - mx + mx * n << '\n';
}
}
除法移位
思路:把在可以移动范围内的最大值移到开头,真无语了,这种题
void solve() {
int n, k; cin >> n >> k;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
int pos = 0, mx = a[1];
for (int i = n, cnt = 0; i >= 1; i ++) {
cnt ++;
if (mx < a[i]) {
mx = a[i];
pos = i;
}
if (cnt == k) break;
}
if (mx == a[1]) cout << 0 << '\n';
else cout << n - pos + 1 << '\n';
}
图上计数(Easy)
思路:将全部边删除,合并乘开两个很接近的数相乘
void solve() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i ++) {
int u, v; cin >> u >> v;
}
cout << ((n + 1) / 2) * (n / 2) << '\n';
}