Codeforces Round #805 (Div. 3)

Codeforces Round #805 (Div. 3)

这场切的真爽,感觉后面题偏简单了。

ABCD

C 维护数字第一次和最后出现位置。

D 贪心从最大的开始删,每次删全部,如果删多了,说明只要删当前字符一部分就好,这个二分找一下。

E

题意

\(n\) 张多米诺牌,正反两个数字,范围 \([1,n]\)。现在要求对它们分为两组,使得它们每组都有\([1,n]\) 中所有数字。

思路

尝试贪心无果后,考虑建边。我们对每个牌正反面的数字建边。

首先,显然每个数字必须出现 \(2\) 次,也就是每个点度数为 \(2\)

然后,如果我们可以把这些牌的正反面分为两个部分,也就是一个二分图。那么我们就可以安排出一组解,又因为度数为 \(2\),第二组解自然也安排出来了。

反之,如果不是二分图,那么一定有奇环,这意味着会有“一个数字同时在正反两面” 的情况。显然这是不合法的。

由上只要对图和度数做判定即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n; cin >> n;
    vector<vector<int>> adj(n + 1);
    bool ok = 1;
    vector<int> du(n + 1);
    map<PII,bool> mp;
    for(int i = 0;i < n;i ++) {
        int u,v; cin >> u >> v;
        if(u == v) {
            ok = 0;
        }
        if(u > v) swap(u,v);
        du[u] += 1,du[v] += 1;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    for(int i = 1;i <= n;i ++) if(du[i] != 2) ok = 0;
    if(!ok) {
        cout << "NO\n";
        return;
    }
    vector<int> col(n + 1);
    function<bool(int,int)> dfs = [&](int u,int c) {
        col[u] = c;
        for(auto v : adj[u]) {
            if(col[u] == col[v]) return false;
            if(!col[v] && !dfs(v,3 - c)) return false;
        }
        return true;
    };
    // rep(i,1,n) cout << col[i] << " \n"[i == n];
    for(int i = 1;i <= n;i ++) {
        if(col[i] == 0) {
            if(!dfs(i,1)) {
                cout << "NO\n";
                return;
            }
        }
    }
    cout << "YES\n";
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

F

题意

两个可重集合 \(a,b\)。我们希望对 \(b\) 中数字做如下操作,使得其变为 \(a\)

  • \(op1\)\(x := 2x\)
  • \(op2\)\(x:= \lfloor \frac{x}{2} \rfloor\)

问可行性

思路

我们可以认为,如果两个数字仅差 二的某次幂 那么这两个数字可以认为是 本质一样的。因此直接把它们所有的因子 \(2\) 先给扬了。

现在剩下一堆奇数。如果 \(b\) 中一个数字不能和 \(a\) 的某个数字匹配,就不断除二。

此时应该贪心的一找到可匹配数字就进行匹配。

这样匹配还有一个问题,比如 15 可以通过除二和 7 ,3 匹配,那么如果 \(a\) 中同时有 7,3 我们该如何安排匹配呢。

也就是说会不会存在 \(x\) 使得产生 \((15,3),(x,7)\) 的匹配使得答案由不可行变为可行。答案是否定的。

因为如果存在,显然 \(x \ge 15\)

\(x = 15\) ,贪心解仍符合。

\(x>15\),如果存在一个比 \(15\) 大的待匹配数字,我们只能使用匹配能力更强的 \(x\) ,这符合贪心策略。如果不存在,贪心解也不会使答案变差。

综上,遇到就匹配就行。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n; cin >> n;
    map<int,int> mp;
    for(int i = 1;i <= n;i ++) {
        int t; cin >> t;
        while(t % 2 == 0) t /= 2;
        mp[t] += 1;
    }
    bool ok = 1;
    for(int i = 1;i <= n;i ++) {
        int t; cin >> t;
        while(t % 2 == 0) t /= 2;
        while((mp.count(t) == 0 or mp[t] == 0) and t > 0) t /= 2;
        if (t > 0) mp[t] -= 1;
        else ok = 0;
    }   

    cout << (ok ? "YES\n" : "NO\n");
    
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

G12

题意

给一棵树,\(q\) 次询问,每次给一堆关键点,问是否有一条简单路径,经过所有关键点。

思路

对G1,\(O(qn)\) 就可以过。因此就是一个分类讨论的事情。

我们发现,当一个子树的儿子中超过2个有关键点就寄了。如果有两个儿子,并且在这个子树外面再有一个点也寄了。我们搜索判断就行。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n; cin >> n;
    vector<vector<int>> adj(n + 1);
    for(int i = 1;i <= n - 1;i ++) {
        int u,v; cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    // cout << "YES";return;
    int q; cin >> q;
    while(q --) {
        vector<int> col(n + 1),cnt(n + 1);
        int m; cin >> m;
        for(int i = 0;i < m;i ++){
            int x; cin >> x;
            col[x] = 1;
        }
        bool flg0 = 0,flg1 = 0;
        function<void(int,int)> dfs = [&](int u,int fa) {
            int ks = 0;
            for(int v : adj[u]) if(v != fa) {
                dfs(v,u);
                if(cnt[v] > 0) ks += 1;
                cnt[u] += cnt[v];
            }
            // cout << u << " " << ks << endl;
            bool innow = 0;
            if(ks > 2) {
                flg0 = flg1 = 1;
            }else if(ks == 2) {
                if(flg0 == 0) flg0 = 1,innow = 1;
                else flg1 = 1;
            }else if(ks == 1) {
                
            }else {

            }
            if(col[u] and flg0 and not innow) flg1 = 1;
            cnt[u] += col[u];
        };
        dfs(1,0);
        if(flg1) cout << "NO\n";
        else cout << "YES\n";
    }
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    // int T;cin>>T;
    // while(T--)
        solve();

    return 0;
}

对G2,\(q\) 很大,需要预处理。我们在总结一下G2的结论。

其实就是如果两个点简单路径外还有一个点就寄了。

两点的简单路径长度可以用lca求。

然后翻译一下这个条件和路径长度的关系,其实就是如果 \(A->B\) 的路径长度不等于 \(A->C->B\) 的路径长度,并且我们不用枚举 \(A,B\) 。因为它们可以取成深度最深点,和离他最远的点。可以想到所有的 \(C\) 都应该在这条简单路径上。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n; cin >> n;
    vector<vector<int>> adj(n + 1);
    for(int i = 1;i <= n - 1;i += 1) {
        int u,v; cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    vector<int> dep(n + 1);
    vector<vector<int>> par(n + 1,vector<int>(21));
    function<void(int,int)> dfs0 = [&](int u,int fa) {
        dep[u] = dep[fa] + 1;
        par[u][0] = fa;
        for(int j = 1;(1 << j) < dep[u];j += 1) {
            par[u][j] = par[par[u][j - 1]][j - 1];
        }
        for(auto v : adj[u]) if(v != fa) dfs0(v,u);
    };
    dfs0(1,0);
    auto lca = [&](int x,int y) {
        if(dep[x] < dep[y]) swap(x,y);
        for(int i = 20;i >= 0;i -= 1) {
            if(dep[par[x][i]] >= dep[y]) {
                x = par[x][i];
            }
        }
        if(x == y) return x;
        for(int i = 20;i >= 0;i -= 1) {
            if(par[x][i] != par[y][i]) {
                x = par[x][i];
                y = par[y][i];
            }
        }
        return par[x][0];
    };
    int q; cin >> q;
    while(q --) {
        auto dis = [&](int x,int y) {
            return dep[x] + dep[y] - 2 * dep[lca(x,y)];
        };
        int m; cin >> m;
        int beg = 0,mxd = 0;
        vector<int> s(m + 1);
        for(int i = 0;i < m;i ++) {
            cin >> s[i + 1];
            if(dep[s[i + 1]] > mxd) {
                mxd = dep[s[i + 1]];
                beg = s[i + 1];
            }
        }
        int end = 0,mxdis = -1;
        for(int i = 1;i <= m;i += 1) {
            if(dis(beg,s[i]) > mxdis) {
                mxdis = dis(beg,s[i]);
                end = s[i];
            }
        }
        bool ok = 1;
        for(int i = 1;i <= m;i ++) {
            if(dis(beg,s[i]) + dis(s[i],end) != mxdis) {
                ok = 0;
                break;
            }
        }
        cout << (ok ? "YES" : "NO") << endl;
    }
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}

差1题ak,但G2也不难,第二天一早就过了。天太晚,脑子越来越糊涂。

posted @ 2022-07-11 12:31  Mxrurush  阅读(40)  评论(0)    收藏  举报