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;
}
原文地址:https://www.cnblogs.com/limil/p/15374485.html