• Codeforces Round #746 (Div. 2)


    Codeforces Round #746 (Div. 2)

    A Gamer Hemose

    题目

    你有 (n) 种武器,每种武器使用一次可以造成 (a_i) 的伤害,并且同一种武器不能连续用两次(但是可以重复使用)。现在有一个 HP 为 (H) 的遗迹守卫,问你最少多少次A掉它。

    思路

    显然

    代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int read() {
        int re = 0;
        char c = getchar();
        bool negt = false;
        while(c < '0' || c > '9')
            negt |= (c == '-')  , c = getchar();
        while(c >= '0' && c <= '9')
            re = (re << 1) + (re << 3) + c - '0' , c = getchar();
        return negt ? -re : re;
    }
    template <char l , char r>
    char readc() {
        char c = getchar();
        while(c < l || c > r)c = getchar();
        return c;
    }
    
    #define int long long
    void solve() {
        int n , d1 = 0 , d2 = 0 , h;
        n = read() , h = read();
        for(int i = 1 ; i <= n ; i++) {
            int d = read();
            if(d > d1)d2 = d1 , d1 = d;
            else if(d > d2) d2 = d;
        }
        int ans = h / (d1 + d2) * 2;
        h %= (d1 + d2);
        if(h != 0)
            ans += (h > d1 ? 2 : 1);
        printf("%lld
    " , ans);
    }
    signed main() {
        int T = read();
        while(T--)solve();
        return 0;
    }
    
    

    B Hemose Shopping

    题目

    给你两个数 (n, x),代表有 (n) 个元素。

    然后输入 (n) 个元素。

    现在问你,能否通过交换两个距离大于等于 (x) 的数,使得数组可以按照非递减的顺序来排序。

    如果可以,输出 YES,否则,输出 NO

    注:(a)(b) 的距离是:( lvert a - b vert)

    思路

    首先,明白一个事情:设三元组((x,y,z)),我们假设(x,y)不能互换,(x,z)(y,x)之间可以互换,则((x,y,z) o(z,y,x) o(z,x,y) o (y,x,z))相当于(x,y)可以互换.

    首先,我们求出一个(l),使得(forall iin[1,l]),(a_i)(a_n)可以互换.

    再求出一个(r),使得,(forall iin[r,n]),(a_i)(a_1)可以互换.

    则相当于([1,l]),([r,n])内的数两两可以互换,([l+1,r-1])内的数不能和任何一个数互换.

    所以,若两个区间有交集,相当于全序列可以两两交换.

    中间不能换的部分必须和排序后的数组一致.

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int read() {
        int re = 0;
        char c = getchar();
        bool negt = false;
        while(c < '0' || c > '9')
            negt |= (c == '-')  , c = getchar();
        while(c >= '0' && c <= '9')
            re = (re << 1) + (re << 3) + c - '0' , c = getchar();
        return negt ? -re : re;
    }
    template <char l , char r>
    char readc() {
        char c = getchar();
        while(c < l || c > r)c = getchar();
        return c;
    }
    
    const int N = 1e5 + 10;
    int n , x;
    int a[N] , b[N];
    void solve() {
        n = read() , x = read();
        for(int i = 1 ; i <= n ; i++)
            b[i] = a[i] = read();
        sort(b + 1 , b + n + 1);
        int l = n - x , r = x + 1;
        // if(l >= r)puts("YES");
        for(int i = l + 1 ; i < r ; i++)
            if(a[i] != b[i]) {
                puts("NO");
                return;
            }
        puts("YES");
    }
    int main() {
        int T = read();
        while(T--)solve();
        return 0;
    }
    

    C Bakry and Partitioning

    题目

    一棵树有 (n) 个节点,第 (i) 个节点的点权为 (a_i) 。(注:树是一个有 (n) 个节点、(n-1) 条边的连通图)

    你需要回答:能不能选择这棵树中的至少 (1) 条边、至多 (k-1) 条边删除,使得删除完这些边的树满足以下条件:

    • 每个联通块的点权异或和相等

    思路

    首先,若最后有解,一定有一种划分方案,使得最后连通块的数量不超过(3).

    证明:三个点权异或和相同的连通块合并后得到新连通块,新连通块的点权异或和不变,(xoplus xoplus x=0oplus x=x).因此,我们可以每次减少两个连通块至连通块的数量等于3或等于2.

    所以,情况就剩下以下三种(设(a)为全树点权的异或和):

    A为全树,B为子树,分为B以及B以外两个连通块,点权异或和分别为(b,aoplus b).

    [aoplus b=b\ aoplus (boplus b)=boplus b\ a=0 ]

    同理,两图分别有(aoplus b=boplus c=c)(aoplus boplus c=b=c),分别解得(b=0,a=c)(a=b=c).

    代码

    #include <iostream>
    #include <cstdio>
    #include <map>
    using namespace std;
    int read() {
        int re = 0;
        char c = getchar();
        bool negt = false;
        while(c < '0' || c > '9')
            negt |= (c == '-')  , c = getchar();
        while(c >= '0' && c <= '9')
            re = (re << 1) + (re << 3) + c - '0' , c = getchar();
        return negt ? -re : re;
    }
    template <char l , char r>
    char readc() {
        char c = getchar();
        while(c < l || c > r)c = getchar();
        return c;
    }
    
    const int N = 1e5 + 10;
    
    struct Edge {
        int to , nxt;
    } ed[N * 2];
    int head[N];
    
    int edg_cnt;
    void addedge(int u , int v) {
        int cnt = ++edg_cnt;
        ed[cnt].to = v , ed[cnt].nxt = head[u] , head[u] = cnt;
    }
    
    int n , k;
    int sum;
    int a[N];
    
    bool ans;
    
    using pr = pair<bool , int>;
    pr dfs(int x , int fa) {//返回值第一维表示当前子树有(true)/无(false)和全树点权异或和相同的子树,第二位表示当前子树的点权异或和.
        pr res = (pr) {false , a[x]};
        for(int i = head[x] ; i ; i = ed[i].nxt) {
            int to = ed[i].to;
            if(to == fa)continue;
            pr tmp = dfs(to , x);
            if(tmp.first && res.first)ans = true;
            res.first |= tmp.first , res.second ^= tmp.second;
        }
        ans |= res.first && (res.second == 0);
        res.first |= (res.second == sum);
        return res;
    }
    void solve() {
        n = read() , k = read();
        
        edg_cnt = 0;
    	sum = 0;
        for(int i = 1 ; i <= n ; i++)
        	head[i] = 0;
        for(int i = 0 ; i <= n * 2 ; i++)
        	ed[i].to = ed[i].nxt = 0;
        	
        for(int i = 1 ; i <= n ; i++)
            sum ^= (a[i] = read());
        for(int i = 1 ; i < n ; i++) {
            int u = read() , v = read();
            addedge(u , v) , addedge(v , u);
        }
        if(sum == 0) {
            puts("YES");
            return ;
        }
        if(k == 2) {
            puts("NO");
            return ;
        }
        ans = false;
        dfs(1 , 0);
        puts(ans ? "YES" : "NO");
    }
    int main() {
        int T = read();
        while(T--)solve();
        return 0;
    }
    
    

    D Hemose in ICPC ?

    题目

    给一棵 (n) 个点的树,定义 (Dist(u,v))(u o v) 路径上的边构成的边权集合的 (gcd),且 (u eq v)

    每一你可以询问交互库 (x) 个点的点集 (X),交互库会返回 (X) 中,(max{Dist(u,v)}, u,v in X) 。也就是说,交互库会找到 (X)(Dist) 最大的一个点对 ((u,v)) 并且返回它们的 (Dist)

    最多可以询问交互库 (12) 次。你需要找到整棵树中 (Dist(u,v)) 最大的那个点对 ((u,v))。若有多个,任意一个都合法。

    思路

    首先一点,交互器回答(gcd)和回答(max)无异.

    然后一点,

    最多可以询问交互库 (12)

    赤裸裸的(log n).

    直接二分即可.

    然后就是如何将树均匀分为联通的两部分,其实欧拉序(父->子树->父->子树->父的顺序)可以实现.

    代码

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    using namespace std;
    int read() {
        int re = 0;
        char c = getchar();
        bool negt = false;
        while(c < '0' || c > '9')
            negt |= (c == '-')  , c = getchar();
        while(c >= '0' && c <= '9')
            re = (re << 1) + (re << 3) + c - '0' , c = getchar();
        return negt ? -re : re;
    }
    template <char l , char r>
    char readc() {
        char c = getchar();
        while(c < l || c > r)c = getchar();
        return c;
    }
    
    const int N = 1e3 + 10;
    struct Edge {
        int to , nxt;
    } ed[N * 2];
    int head[N];
    void addedge(int u , int v) {
        static int cnt = 0;
        ++cnt;
        ed[cnt].to = v , ed[cnt].nxt = head[u] , head[u] = cnt;
    }
    
    int n;
    int id[N * 2];
    
    void dfs(int x , int fa) {
        static int cnt = 0;
        id[++cnt] = x;
        for(int i = head[x] ; i ; i = ed[i].nxt) {
            int to = ed[i].to;
            if(to == fa)continue;
            dfs(to , x);
            id[++cnt] = x;
        }
    }
    
    int ask(vector<int> &node) {
        cout << '?' << ' ' << node.size() << ' ';
        for(int i : node)
            cout << i << ' ';
        cout << endl;
        node.clear();
        int res;
        cin >> res;
        return res;
    }
    
    vector<int> node;
    
    void add(int l , int r) {
        static bool vis[N];
        memset(vis , 0 , sizeof(vis));
        for(int i = l ; i <= r ; i++)
            if(!vis[id[i]]) {
                vis[id[i]] = true;
                node.push_back(id[i]);
            }
    }
    int main() {
        ios::sync_with_stdio(false);
        cin >> n;
        for(int i = 1 ; i < n ; i++) {
            int u , v;
            cin >> u >> v;
            addedge(u , v) , addedge(v , u);
        }
        for(int i = 1 ; i <= n ; i++)node.push_back(i);
        int maxVal = ask(node);
        dfs(1 , 0);
        int l = 1 , r = n * 2 - 1;
        while(l + 1 < r) {
            int mid = (l + r) / 2;
            add(l , mid);
            if(ask(node) == maxVal) r = mid;
            else l = mid;
        }
        cout << "! " << id[l] << ' ' << id[r] << endl;
        return 0;
    }
    
    
  • 相关阅读:
    JAVA基础之(十三)“集合”
    JAVA基础之(十二)“泛型”
    JAVA基础之(五)”继承“
    算法--验证时间段不交叉/不重复的方法
    工具类--常量类
    工具--常见eclipse配置导入web工程(tomcat容器)步骤
    jar包--POI.jar包使用问题汇总
    java中常忽视的小问题
    ResultSetMetaData
    PreparedStatement
  • 原文地址:https://www.cnblogs.com/dream1024/p/15547709.html
Copyright © 2020-2023  润新知