Codeforces Round #629(div3)题解

5 篇文章 0 订阅

比赛链接

D
题意:
给你n个数(形成一个换),现在要让你给这n个数上色,只有一个限制:如果相邻的两个数不同,那他们俩的的颜色也要不同。
现在问你最少需要颜色的数量,然后输出结果。

思路:
感觉没几种情况,分类讨论一下就过了。
1.如果所有数都相等只要一种颜色。
2.如果长度n为偶数的话,只要两种颜色,1 2 1 2…这样排就完事了。
那我们接下来是要考虑n为奇数的情况。
现在假设n个数的编号为1~n, 每个数为t[i] (1<= i <= n)
1.如果t[i] == t[n], 那这个限制条件就跟他没有关系,1 2 1 2…这样排就完事。
2.如果t[i] != t[n-1], 如果我们还是像之前那么1 2 1 2的排就有问题了,因为长度为奇数,最后一个数的颜色是1, 第一个数的颜色也是1(因为是个环嘛,所以他们两个相邻), 而他们两个数不同,而我们唯一要做的就是让相邻两个数,如果数不同的话染的颜色也要不同,那我就想可不可以让中间有一个11或者22的,这样第一个和最后一个数的颜色就不同了, 那么能变成11或者22的唯一条件就是两个数相同,然后就没了。
比赛写的代码太丑了就不放了 :>

E
题意:
给一个数,然后告诉你k个点,问你能不能找出一条链,这k个点要么在这条链上,要么和这条链距离为1。

思路:
原来dfs序还能这么用,学到了,之前dfs序只用来搞过线段树, 现在知道dfs序还能判断一个点在不在另一个点的子数上(其实本来就是有那层含义,但dfs序放在线段树的题里是要找出一个点子树上的所有点)。
先找到最深的那个点,然后把给的除了那个点的其他点都变成他的父亲节点,然后看这些点在不在一条链上,注意这两者的顺序不能颠倒,这里给个例子:
三条边 (1, 2) (1, 3)(1, 4), 问你点2,3,4符不符合条件。

dfs序:

/**
* Think twice, code once.
* 1.integer overflow(maybe even long long overflow : (a+b >= c) -> (a >= c-b)
* 2.runtime error
* 3.boundary condition
* ---------------------------------------------------------------------------------------
* Author : zzy
* Date : 2020-03-28-23.21.21 Saturday
*/
#include <bits/stdc++.h>

#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
#define for1(i, n) for (int i = 1; i <= (int)(n); ++i)
#define ford(i, a, b) for (int i = (int)(a); i >= (int)b; --i)
#define fore(i, a, b) for (int i = (int)(a); i <= (int)(b); ++i)
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define ms(x, y) memset(x, y, sizeof(x))
#define SZ(x) ((int)(x).size())

using namespace std;

typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<pii> vpi;
typedef vector<vi> vvi;
typedef long long i64;
typedef vector<i64> vi64;
typedef vector<vi64> vvi64;
typedef pair<i64, i64> pi64;
typedef double ld;

template<class T> bool uin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool uax(T &a, T b) { return a < b ? (a = b, true) : false; }

const int maxn = 2*(int)1e5+1000;
int n, m, tot = 1, vis[maxn], in[maxn], out[maxn], pa[maxn], dep[maxn];
vvi g(maxn);

bool belong(int a, int b) {
    if (in[a] <= in[b] && in[b] <= out[a]) return true;
    return false;
}
void dfs(int u, int par) {
    in[u] = tot++;
    if (par) pa[u] = par;
    if (par) dep[u] = dep[par]+1;
    for (int to : g[u]) {
        if (to == par) continue;
        dfs(to, u);
    }
    out[u] = tot;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.precision(10);
    cout << fixed;
#ifdef LOCAL_DEFINE
    freopen("input.txt", "r", stdin);
#endif

    cin >> n >> m;
    for1(i, n) pa[i] = i;
    forn(i, n-1) {
        int u, v;
        cin >> u >> v;
        g[u].eb(v);
        g[v].eb(u);
    }
    ms(vis, 0);
    ms(dep, 0);
    dfs(1, 0);
    forn(i, m) {
        bool ok = true;
        int k;
        cin >> k;
        vector< pair<int, int> > v(k);
        forn(i, k) {
            int x;
            cin >> x;
            v[i] = {dep[x], x};
        }
        sort(all(v));
        int leaf = v[k-1].se;
        for (int i = 0; i < k-1; ++i) {
            v[i].se = pa[v[i].se];
        }
        for (int i = 0; i < k-1; ++i) {
            if (!belong(v[i].se, leaf)) {
                ok = false;
                break;
            }
        }
        cout << (ok?"YES":"NO") << '\n';
    }

#ifdef LOCAL_DEFINE
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}

LCA:dfs+ST表写法

/**
* Author : zzy
* Date : 2020-04-16-15.49.41 Thursday
*/
#include <bits/stdc++.h>

#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
#define for1(i, n) for (int i = 1; i <= (int)(n); ++i)
#define ford(i, a, b) for (int i = (int)(a); i >= (int)b; --i)
#define fore(i, a, b) for (int i = (int)(a); i <= (int)(b); ++i)
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define ms(x, y) memset(x, y, sizeof(x))
#define SZ(x) ((int)(x).size())

using namespace std;

typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<pii> vpi;
typedef vector<vi> vvi;
typedef long long i64;
typedef vector<i64> vi64;
typedef vector<vi64> vvi64;
typedef pair<i64, i64> pi64;
typedef double ld;

template<class T> bool uin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool uax(T &a, T b) { return a < b ? (a = b, true) : false; }

const int maxn = 4*(int)1e5+5; //欧拉序是两倍的点数量的多少
int loc[maxn],num[maxn],dep[maxn],LOG[maxn];
int dp[25][maxn], point[25][maxn];          //2^20 == 1e6  2^25 == 3e7
int par[maxn], val[maxn];
int all = 0;
int head[maxn], tot=0;
struct Edge{
    int nxt, to, val;
}edge[maxn];
int tc, n, m;

int read() {
    int ans = 0, f = 1; char c = getchar();
    for (;c < '0' | c > '9'; c = getchar()) if (c == '-') f = -1;
    for (;c >= '0' && c <= '9'; c = getchar()) ans = ans * 10 + c - '0';
    return ans * f;
}
void addEdge(int x,int y) {
    edge[tot].to = y;
    edge[tot].nxt = head[x];
    head[x] = tot++;
}
void dfs(int u,int now) {
    loc[u]=++all; num[all]=u; dep[all]=now;
    for (int i = head[u]; ~i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        if (loc[v]) continue;
        dfs(v,now+1);
        num[++all]=u;
        dep[all]=now;
    }
}
void initRMQ() {
  LOG[0] = -1;
  for(int i = 1;i <= all; i++) {
      dp[0][i] = dep[i];
      point[0][i] = num[i];
      LOG[i] = ((i&(i-1)) == 0) ? LOG[i-1]+1 : LOG[i-1];
  }
  for (int i = 1; (1<<i) <= all; ++i) {
    for (int j = 1; j+(1<<i)-1 <= all; ++j) {
      if (dp[i-1][j] < dp[i-1][j+(1<<(i-1))]) {
        dp[i][j] = dp[i-1][j];
        point[i][j] = point[i-1][j];
      } else {
        dp[i][j] = dp[i-1][j+(1<<(i-1))];
        point[i][j] = point[i-1][j+(1<<(i-1))];
      }
    }
  }
}
int queryRMQ(int l, int r) {
  l = loc[l]; r = loc[r];
  if (l > r) swap(l, r);
  int k = LOG[r-l+1];
  /*
  貌似下面这种写法更快
  记得把上面预处理的LOG删了
   P 3379

  int k=0;
  while((1<<k)<=r-l+1) k++;
  k--;
  */
  if(dp[k][l] < dp[k][r-(1<<k)+1]) return point[k][l];
  else return point[k][r-(1<<k)+1];
}
void preWork(int u, int fa, int now) {
    val[u] = now;
    par[u] = fa;
    for (int i = head[u]; ~i; i = edge[i].nxt) {
        int to = edge[i].to;
        if (to == fa) continue;
        preWork(to, u, now+1);
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.precision(10);
    cout << fixed;
#ifdef LOCAL_DEFINE
    freopen("input.txt", "r", stdin);
#endif

    ms(head, -1);
    ms(loc, 0);
    ms(par, -1);
    cin >> n >> m;
    forn(i, n-1) {
        int u, v;
        cin >> u >> v;
        addEdge(u, v);
        addEdge(v, u);
    }
    dfs(1, 1);
    initRMQ();
    preWork(1, -1, 1);
    forn(i, m) {
        vector< pair<int, int> > res;
        int k, x;
        cin >> k;
        while (k--) {
            cin >> x;
            if (par[x] != -1) x = par[x];
            res.eb(mp(val[x], x));
        }
        sort(all(res));
        int len = SZ(res);
        bool ok = true;
        for (int i = 0; i < len-1; ++i) {
            int temp = queryRMQ(res[i].se, res[len-1].se);
            if (temp != res[i].se) {
                ok = false;
                break;
            }
        }
        cout << (ok ? "YES" : "NO") << '\n';
    }

#ifdef LOCAL_DEFINE
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}

F
题意:
给你n个数,现在给你两个操作:
1.把这n个数里最大的那个数-1
2.把这n个数里最小的那个数+1
现在问你最少要多少次操作才能得到至少k个相同的数。

思路:
看了这位巨巨的博客,才懂得,假设结果k个相同的数是两个数之间的某个数,付出的代价肯定比取两个数其中的一个数来的大,所以枚举每一个数作为最后的答案,要获得这个数要么全都是左边(先变成a[i]-1)来的,要么全都是右边来(先变成a[i]+1)的,要么两边都有,维护一下答案就完事了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值