首页 > 其他分享 >Codeforces Round 922 (Div. 2)

Codeforces Round 922 (Div. 2)

时间:2024-02-02 11:57:55浏览次数:22  
标签:le leaf int Codeforces pos 叶子 ans 922 Div

https://codeforces.com/contest/1918

题目很有意思。A~D vp中过了,但是太太太慢,亟须复健。E赛后过的,交互题真是难调!F看题解过的

A. Brick Wall *800 用砖头砌墙

有形状 \(1\times k\) 的水平砖和形状 \(k \times 1\) 的竖直砖,要不重不漏地铺满 \(n\times m\) 的区域,问水平砖数量与竖直砖数量之差的最大值。任意两块砖的 \(k\) 都不必相同。

既然不要求砖的规格一样,那就用水平砖直接铺满就行。

cout << n * (m / 2);

B. Minimize Inversions *900

给定两个 \(n\) 的排列,可以任意次交换两个下标,交换时两个数组的对应位置也分别交换,即交换 \(a_i,a_j\) 同时交换 \(b_i,b_j\)。最小化 \(a[]\) 的逆序对数量 \(+\) \(b[]\) 的逆序对数量

类似贪心问题之排序不等式。但实际上单纯地把 \(a[]\) 排个序就行了,因为这样保证了 \(a_i<a_j\),而无论 \(b_i>b_j\) 还是 \(b_i<b_j\),交换 \(i,j\) 都不会让答案更优

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol() {
    int n;
    cin >> n;
    vector<pair<int, int>> p(n);
    for (int i = 0; i < n; i++) cin >> p[i].first;
    for (int i = 0; i < n; i++) cin >> p[i].second;
    sort(p.begin(), p.end());
    for (int i = 0; i < n; i++) cout << p[i].first << " \n"[i == n - 1];
    for (int i = 0; i < n; i++) cout << p[i].second << " \n"[i == n - 1];
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

C. XOR-distance *1400

给定 \(a,b,r\),问 \(|(a\oplus x) - (b\oplus x)|, x\in [0,r]\) 的最小值

1e4组数据,\(0\le a,b,r \le 10^{18}\)

按位考虑,如果a和b某位相同,x取啥都不会对答案产生贡献,为了让x小一点,x这位取0。在a,b相异的最高位h上,x取啥都会给答案带来 \(2^h\),后面的位加起来也没有这个 \(2^h\) 大,后面的位只能尽量稍微缩小一下答案

我们希望 x 的第h位取0以使x较小,同时希望 \((a\oplus x) - (b\oplus x)\ge 0\) 以去掉绝对值符号,因此不妨假设 \(a\ge b\)

然后按位考虑,贪心搞搞即可

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol() {
    ll a, b, r;
    cin >> a >> b >> r;
    if (a < b) swap(a, b);
    ll x = 0, ans = 0;
    for (int i = 61, f = false; ~i; i--) {
        if ((a >> i & 1) == (b >> i & 1)) continue; //相同位不用管
        if (!f) { //a,b相异的第一位
            ans += 1ll << i;
            f = true;
        } else if (a >> i & 1) {
            if (x + (1ll << i) <= r) {
                x += 1ll << i;
                ans -= 1ll << i;
            } else {
                ans += 1ll << i;
            }
        } else {
            ans -= 1ll << i;
        }
    }
    cout << ans << '\n';
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

D. Blocking Elements *1900 二分答案+dp+单调队列优化

给定正整数数组 \(a[]\),可选择任意个下标 \(b_1 < b_2 < \cdots <b_k\),把数组分成一些子段 \([1,b_1-1],[b_1+1,b_2-1],\cdots\)

代价取下面两个东西的最大值:

  • \(a_{b_1}+a_{b_2}+\cdots +a_{b_k}\)
  • 上述子段和的最大值

问最小代价是多少。

\(n\le 1e5, 1\le a_i\le 10^9\)

二分答案,dp判断。\(dp[i]\) 表示考虑 \(1\sim i\),要选 \(i\) ,每个子段和都 \(\le mid\),\(\sum a_{b_i}\) 最小是多少。

不妨假设 \(a_0=a_{n+1}=0\),这俩都要选

\(dp_i\) 能被满足 \(a_{j+1}+\cdots +a_{i-1}\le mid\) 的 \(dp_j\) 更新。我们要找其中最小的

用set或者单调队列维护即可

感觉很适合作为单调队列优化dp的入门

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 5;
ll n, a[N], s[N], f[N];
int q[N], hh, tt;
bool ok(ll mid) {
    hh = 1; tt = 0;
    q[++tt] = 0;
    for (int i = 1; i <= n + 1; i++) {
        while (hh <= tt && s[i - 1] - s[q[hh]] > mid) hh++;
        if (hh > tt) return false;
        f[i] = f[q[hh]] + a[i];
        while (hh <= tt && f[i] <= f[q[tt]]) tt--;
        q[++tt] = i;
    }
    return f[n + 1] <= mid;
}
void sol() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
    }
    s[n + 1] = s[n];
    a[n + 1] = 0;
    ll l = 0, r = 1e15, ans;
    while (l <= r) {
        ll mid = l + r >> 1;
        if (ok(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << ans << '\n';
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

E. ace5 and Task Order *2200 快速排序真神奇

交互题。有未知的排列 \(a[]\) 和数 \(x\),输入 \(n\)。每次询问一个位置 \(i\),若 \(a_i>x\),回答 ">",并且 \(x\) 增大 \(1\);若 \(a_i<x\),回答 "<",并且 \(x\) 减小 \(1\);若相等,回答 "=",\(x\) 不变。请在 \(40n\) 次询问内猜出原排列。

\(1\le x\le n\le 2000\)

  1. 一直问一个位置 \(p\),直至 \(x=p\)
  2. 用 \(2\times 未知位置数\) 的时间问出其他位置与 \(a_p\) 的大小关系,这样也就知道了 \(a_p\) 的值
  3. 类似快速排序,分治处理比 \(a_p\) 小/大的位置

1的次数会不会太多?应该不会吧。。画一下分治的过程,应有左子区间的分治点≤父区间的划分点≤右子区间的分治点,考虑递归过程的dfs树,x在同一层内的移动距离应该 \(\le n\)

当然快排为了不被卡,应该先打乱数组或者每次取随机点。询问次数 \(O(nlogn)\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2010;

random_device rd;
mt19937 gen(rd());

int a[N];

void f(vector<int> &pos, int bond) {
    if (pos.empty()) return;
    if (pos.size() == 1) {
        a[pos[0]] = bond;
        return;
    }
    
    vector<int> L, R;
    int p = gen() % (int)pos.size();
    for (char ch = '?'; ch != '='; ) {
        cout << "? " << pos[p] << endl;
        cin >> ch;
    }
    for (int i = 0; i < (int)pos.size(); i++) {
        if (i == p) continue;
        cout << "? " << pos[i] << endl;
        char ch; cin >> ch;
        (ch == '<' ? L : R).push_back(pos[i]);
        cout << "? " << pos[p] << endl; //把x变回来
        cin >> ch;
    }
    
    a[pos[p]] = bond + (int)L.size();
    
    f(L, bond); f(R, a[pos[p]] + 1);
}

void sol() {
    int n;
    cin >> n;
    
    vector<int> pos(n);
    iota(pos.begin(), pos.end(), 1);
    f(pos, 1);
    cout << "! ";
    for (int i = 1; i <= n; i++) cout << a[i] << ' ';
    cout << endl;
}
int main() {
//    ios::sync_with_stdio(0);
//    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

F. Caterpillar on a Tree *2500 树上毛毛虫

从根出发,每次走到邻点要花 1 时间,可以不超过 k 次从任意点立即回到根,问访问完整棵树的最少时间。

n 2e5, k 1e9

若某点被访问,则它的所有祖先都被访问过,因此访问所有的叶子就够了。从非叶点跳回根肯定不优,应该在访问某叶子后直接跳回根

我们可以把整个过程视为访问每一个叶子的过程,即不断从一个叶子走到另一个叶子的过程。答案由 根到叶子的路径、叶子到叶子的路径、叶子跳回根 这三种过程组成。访问整棵树的最后一个叶子后直接结束即可,不必再回到根

整个过程长这样:\(root \to leaf_1 \to leaf_2 \to \cdots \to leaf_i \to leaf_j \to \cdots \to leaf_{k}\)

把根到叶子的路径用跳根替换掉是没有必要的,我们要做的是把一些从叶子到相邻叶子的路径用跳根替换掉,即把 \(leaf_i \to leaf_j\) 用 \(root \to leaf_j\) 替换掉,也就是把 \(leaf_i\to u \to leaf_j\) 用 \(root\to u \to leaf_j\) 替换掉,其中 \(u\) 为 \(\text{lca}(leaf_i,leaf_j)\)。节省的时间为 \(\Delta_{ans}\) 两个叶子被访问的时间戳之差减去 \(leaf_j\) 的深度

为了使答案更优,我们希望 \(leaf_i\to u\) 尽可能长,因此应该最后访问最深的儿子

注意到叶子数 \(\le n\),因此把所有 \(\Delta_{ans}\) 记下来,取最好的 \(k\) 个即可

官方题解还有个很长的证明,懒得看了qaq

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 4e5 + 5;
int n, k;
vector<int> G[N];

int dep[N], son[N]; //最深儿子
int dfs(int u) { //返回最深叶子的深度
    int ans = dep[u];
    for (int v : G[u]) {
        dep[v] = dep[u] + 1;
        int res = dfs(v);
        if (res > ans) ans = res, son[u] = v;
    }
    for (auto it = G[u].begin(); it != G[u].end(); it++) {
        if (*it == son[u]) {
            swap(*it, G[u].back());
            break;
        }
    }
    return ans;
}

vector<int> ans;
int idx, las = 1; //上一个叶子的访问时间
void euler(int u) {
    if (G[u].empty()) { //遇叶子,尝试把上一个叶子→u的路径用跳到根来替换
        ans.push_back(-(idx - las) + dep[u]);
        las = idx;
    }
    else for (int v : G[u]) {
        ++idx; //访问v
        euler(v);
        ++idx; //回到u
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    cin >> n >> k;
    for (int i = 2; i <= n; i++) {
        int p; cin >> p;
        G[p].push_back(i);
    }
    
    dfs(1);
    euler(1);
    
    sort(ans.begin(), ans.end());
    while ((int)ans.size() > k || !ans.empty() && ans.back() >= 0) ans.pop_back();
    cout << las + accumulate(ans.begin(), ans.end(), 0);
}

G. Permutation of Given *2700 神仙构造题

给定 \(n\),构造数组 \(a[]\),使得数组 \(b[],b_i=a_{i-1}+a_{i+1}\) 是 \(a[]\) 的重排

打表找规律:https://zhuanlan.zhihu.com/p/680665380

官解,想不到的构造:https://www.cnblogs.com/cpchenpi/p/-/CF1918-solutions

标签:le,leaf,int,Codeforces,pos,叶子,ans,922,Div
From: https://www.cnblogs.com/wushansinger/p/18002913

相关文章

  • CF922 div2 D. Blocking Elements
    题面代码点击查看代码#include<bits/stdc++.h>usingnamespacestd;#defineIOSios::sync_with_stdio(0);cin.tie(0);cout.tie(0);#definerep(i,a,n)for(inti=a;i<=n;i++)#defineper(i,a,n)for(inti=n;i>=a;i--)#defineendl"\n"#definefif......
  • js+css 父div,里面有很多子div,当子div在父div中放不下时候,自动滚动子div,向左横向滚动,
    <!DOCTYPEhtml><htmllang="en"><head>  <metacharset="UTF-8">  <metaname="viewport"content="width=device-width,initial-scale=1.0">  <style>    #parentDiv{  ......
  • Codeforces Round 922 (Div. 2)
    CodeforcesRound922(Div.2)ps:25分钟AB都over,C给我打破防了、、、讨厌异或、、、我一直以为是数学结果、、、只能说一怒之下怒了一下A.BrickWall想法:要使得稳定性高,那么就多用1*2的砖块就行(A题可以直接找规律,通过样例)#include<bits/stdc++.h>usingnamespacestd;......
  • Codeforces Round 922 (Div. 2) 赛后总结
    自豪的是D题做出来了,悲哀的是B题没能做出来C题的绝对值最小D题,DP存不下状态就把状态放进所求值中比赛快结束的时候,我想,这个B题,它但凡需要我通过归并排序或者树状数组求逆序对,不比C题进制转化要难?于是我就猜了一个结论结论是对的,但不幸的是,我编程实现的时候出错了考虑怎样证......
  • Codeforces Round 770 (Div. 2)(数学异或奇偶性)
    B.FortuneTelling拿到题目看数据范围之后就知道暴力显然是来不及的。那么只能找性质。\(考虑x和x+3的不同\quad奇偶性不同\)\(然后考虑两种操作对于一个数的奇偶性的影响\)\(加法:同奇偶则运算结果是偶,不同则运算结果为奇\)\(异或:惊奇的发现异或也是这样的\)这样我们就......
  • Codeforces Round 922 (Div. 2)
    基本情况A题当时做完提交一直编译错误后面发现是语言选择错误,浪费了五六分钟,然后去做B没想到去看C看了样例感觉可以做,结果干了好久都没出来,倒回去看B还是没做出来,感觉全程很紧张不知道为什么,脚一直在抖。A.BrickWall没啥好说的,就是全部放竖直的,实在不能放了再放横的而且把横......
  • Codeforces Round 921 (Div. 1)
    Preface被折纸狠狠地腐乳了,但好在手速够快光速写完前两题成功把Ohara_Rinne这个号也打上橙了之后就不开其它小号打了,也差不多该尝试去向上挑战了,不然一直呆在舒适圈内也没啥提升的说A.DidWeGetEverythingCovered?直接把序列自动机建出来,不妨设状态\((x,y)\)表示构造了长......
  • Codeforces Round 922 (Div. 2)
    CodeforcesRound922(Div.2)比赛链接A.BrickWall思路简单的模拟,要想实现最高的稳定性,就横着放就可以了,因为长度必须大于等于2,所以最后即使不能被2整除,也可以算在里面Code#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongvoidsolve(){ intn,......
  • Codeforces Round 922 (Div.2)
    题目链接点这里CF1918ABrickWallvoidsolve(){lln,m;cin>>n>>m;cout<<n*(m/2)<<endl;}CF1918BMinimizeInversions注意到,当其中一个排列有序时,总的逆序对数量最少()今天找个时间补上证明对于任意一对\(i,j\)位置,其可能的逆序对总......
  • Codeforces Round 922 (Div. 2) A-C
    这次还好,虽然还是不够满意,因为D题没写出来。。A一个明显的贪心,都竖着放就好了#include<bits/stdc++.h>#definelllonglongusingnamespacestd;inlineintread(){ charc=getchar();inta=0,b=1; for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1; for(;c......