• codeforces round 746 div2 C-E


    source

    C - Bakry and Partitioning(贪心)

    假设所有点的异或和为(x),然后被拆成了(m)个联通块,由于各个联通块的异或值都相同,有

    [x_1 oplus x_2 oplus ... oplus x_m = x ]

    可得每个联通块的值必须都相等。所以如果(x=1),答案为YES(删除任意一条边即可);否则直接贪心dfs自底向上找是否有子树异或值为(x)的,找到就删掉。只要个数大于等于2,答案就是YES。

    #include <bits/stdc++.h>
    
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 3e5 + 10;
    const double eps = 1e-5;
    
    int arr[N];
    int val[N];
    int cnt;
    vector<int> np[N];
    
    void dfs(int p, int fa, int x) {
        val[p] = arr[p];
        for(int nt : np[p]) {
            if(nt == fa) continue;
            dfs(nt, p, x);
            val[p] ^= val[nt];
        }
        if(val[p] == x) {
            cnt++;
            val[p] = 0;
        }
    }
    
    int main() {
        IOS;
        int t;
        cin >> t;
        while(t--) {
            int n, k;
            cin >> n >> k;
            k--;
            int x = 0;
            for(int i = 1; i <= n; i++) {
                np[i].clear();
                cin >> arr[i];
                x ^= arr[i];
            }
            for(int i = 1; i < n; i++) {
                int u, v;
                cin >> u >> v;
                np[u].push_back(v);
                np[v].push_back(u);
            }
            cnt = 0;
            dfs(1, 0, x);
            if(x == 0) cout << "YES" << endl;
            else {
                if(k <= 1) cout << "NO" << endl;
                else {
                    if(cnt >= 2) cout << "YES" << endl;
                    else cout << "NO" << endl;
                }
            }
        }   
    }
    

    D - Hemose in ICPC (欧拉序)

    每个询问本质就是求最大边权。询问数这么少,显然是二分。但是不能直接二分点集,必须保证点集是连通的并且划分尽量均匀。

    原做法就是找重心,然后将点尽量等分成两个联通块,二分。写起来又臭又长。

    #include <bits/stdc++.h>
    
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    typedef pair<int, int> PII;
    const int N = 3e5 + 10;
    const double eps = 1e-5;
    vector<int> np[N];
    int cnt[N];
    int cntq;
    
    void dfs(int p, int fa) {
        cnt[p] = 1;
        for(int nt : np[p]) {
            if(nt == fa) continue;
            dfs(nt, p);
            cnt[p] += cnt[nt];
        }
    }
    
    int mid, tar;
    vector<int> pre, las;
    void getask(int p, int fa, int tot) {
        vector<PII> num;
        if(fa) num.push_back({tot - cnt[p], fa});
        for(int nt : np[p]) {
            if(nt == fa) continue;
            num.push_back({cnt[nt], nt});
        }
        if(num.size() > 1) {
            sort(num.begin(), num.end());
            int sum = 0, mip = 0, dd = INF;
            for(int i = 0; i < num.size() - 1; i++) {
                sum += num[i].first;
                int d = abs(tot - 1 - 2 * sum);
                if(d < dd) {
                    dd = d;
                    mip = i;
                }
            }
            if(dd < mid) {
                mid = dd;
                tar = p;
                pre.clear();
                las.clear();
                for(int i = 0; i <= mip; i++) pre.push_back(num[i].second);
                for(int i = mip + 1; i < num.size(); i++) las.push_back(num[i].second);
            }
        }
        for(int nt : np[p]) {
            if(nt == fa) continue;
            getask(nt, p, tot);
        }
    }
    vector<int> asknum;
    int ask() {
        cntq++;
        assert(cntq <= 12);
        cout << "? " << asknum.size();
        for(int v : asknum) cout << " " << v;
        cout << endl;
        asknum.clear();
        int res;
        cin >> res;
        return res;
    }
    
    void pushtree(int p, int fa) {
        for(int nt : np[p]) {
            if(nt == fa) continue;
            asknum.push_back(nt);
            pushtree(nt, p);
        }
    }
    
    PII solve(int n, int mx) {
        int rt = 1;
        while(1) {
            dfs(rt, 0);
            if(cnt[rt] <= 2) break;
            mid = INF;
            getask(rt, 0, cnt[rt]);
            asknum.push_back(tar);
            for(int p : pre) {
                asknum.push_back(p);
                pushtree(p, tar);
            }
            if(ask() == mx) np[tar] = pre;
            else np[tar] = las;
            rt = tar;
        }
        return {rt, np[rt].front()};
    }
    
    
    int main() {
        int n;
        cin >> n ;
        for(int i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            np[u].push_back(v);
            np[v].push_back(u);
        }
        for(int i = 1; i <= n; i++) asknum.push_back(i);
        int mx = ask();
        PII ans = solve(n, mx);
        cout << "! " << ans.first << " " << ans.second << endl;
    }
    

    正解是欧拉序。所谓欧拉序就是沿着树外围走一圈经过再回到原点的结点序列,它和dfs序非常相似。例如样例1的欧拉序为:1 2 3 2 4 2 1 5 6 5 1。欧拉序中相邻两个点可以看作一条边,你会发现,在欧拉序中,任意一个区间内的点,它们都是连通的(因为是连续经过的)!因此直接按照欧拉序二分点集即可。

    最大询问次数(1 + log 2n = 2+log 1000 le 12)

    #include <bits/stdc++.h>
    
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 3e5 + 10;
    const double eps = 1e-5;
    
    
    vector<int> np[N];
    vector<int> pos;
    
    void euler(int p, int fa) {
        pos.push_back(p);
        for(int nt : np[p]) {
            if(nt == fa) continue;
            euler(nt, p);
            pos.push_back(p);
        }
    }
    
    int main() {
        int n;
        cin >> n;
        for(int i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            np[u].push_back(v);
            np[v].push_back(u);
        }
        cout << "? " << n;
        for(int i = 1; i <= n; i++) cout << " " << i;
        cout << endl;
        int mx;
        cin >> mx;   
        euler(1, 0);
        int len = pos.size() - 1;
        int l = 0, r = len - 1;
        while(l <= r) {
            int mid = (l + r) / 2;
            set<int> s;
            for(int i = mid; i <= len; i++) {
                s.insert(pos[i]);
            }
            cout << "? " << s.size();
            for(auto p : s) cout << " " << p;
            cout << endl;
            int res;
            cin >> res;
            if(res == mx) {
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        cout << "! " << pos[r] << " " << pos[r + 1] << endl;
    }
    

    E - Bored Bakry(位,思维)

    假设(x=a_l & a_{l+1} & ... & a_r)(y=a_l oplus a_{l+1}oplus ... oplus a_r)。如果区间长度为奇数,那么(y)的二进制位中至少包含(x),即(xle y),所以区间长度不能是奇数;如果区间长度为偶数,那么(y)的二进制位中,(x)为1的位置(y)必定为0,这意味着(x eq y)。如果要(xge y),那么(x)最高位1以上的位置中(y)不能为1。

    故直接拆位,枚举(x)的最高位,找到当前位下连续的1的区间(这样的区间与起来这一位才能为1),统计区间中最高位以上异或为0的最长区间是多长,这个直接用异或前缀和(O(n))搞定。

    总时间复杂度(O(nlog m))(m)为值域。

    小坑:

    • 值域要稍微开大一点,因为是二进制,异或出来可能会比最大值稍大。
    #include <bits/stdc++.h>
    
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 2e6 + 10;
    const double eps = 1e-5;
    
    int vxor[N];
    int prexor[N];
    int arr[25][N];
    int pos[2][N];
    
    int cal(int l, int r) {
        vector<int> tar;
        prexor[l - 1] = 0;
        for(int i = l; i <= r; i++) {
            prexor[i] = vxor[i];
            prexor[i] ^= prexor[i - 1];
        }   
        for(int i = l - 1; i < r; i++) {
            if(pos[i % 2][prexor[i]] == -1) {
                pos[i % 2][prexor[i]] = i;
                tar.push_back(prexor[i]);
            } 
        }
        int res = 0;
        for(int i = l; i <= r; i++) {
            int p = pos[i % 2][prexor[i]];
            if(p != -1) {
                res = max(res, i - p);
            }
        }
        for(int p : tar) pos[0][p] = pos[1][p] = -1;
        return res;
    }
    
    int main() {
        memset(pos, -1, sizeof pos);
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++) {
            int x;
            cin >> x;
            for(int j = 0; j <= 22; j++) {
                arr[j][i] = (bool)(x & (1 << j));
            }
        }
        int ans = 0;
        for(int i = 22; i >= 0; i--) {
            int p = 1;
            while(1) {
                while(p <= n && !arr[i][p]) p++;
                if(p > n) break;
                int l = p;
                while(p <= n && arr[i][p]) p++;
                int r = p - 1;
                ans = max(ans, cal(l, r));
            }
            for(int j = 1; j <= n; j++) {
                vxor[j] ^= (arr[i][j] << i);
            }
            
        }
        cout << ans << endl;
    }
    
  • 相关阅读:
    上传和下载附件功能
    C#小常识,持续更新..
    动态添加HTML表单控件,无(runat="server")
    Excel技巧 持续更新..
    JS函数集锦 持续更新..
    JS 函数 检验输入是否为数字类型,正整数
    存储过程 游标 事例
    Sql 查询语句中的类型转换
    shell 计数脚本
    centos 获取文件的创建时间
  • 原文地址:https://www.cnblogs.com/limil/p/15374485.html
Copyright © 2020-2023  润新知