ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1033E 做题体验

2022-07-20 02:02:36  阅读:175  来源: 互联网

标签:std chose fa CF1033E void int 体验 include


题目链接

这题看上去一脸不可做,对,我看什么题都不可做。。。
然后瞄一眼题解,发现一个小 \(\tt Trick\) :
判定二分图可以先拉出一个生成树,対生成树进行染色然后看相同颜色内有没有连边。

所以现在的第一步是拉出一个生成树。
首先,我们先把题目中要求的交互函数写出来,我用一个 \(\tt vector\) 记录查询的点集。
同时在我自己测试时发现可能会询问重复的点集,所以用一个 \(\tt map\) 来记录已经查过的答案。

inline int ask(std::vector<int> chose) {
  if (chose.size() == 1) return 0;
  std::sort(chose.begin(), chose.end());
  if (ma[chose]) return ma[chose];
  printf("? ");
  write(chose.size()), Enter;
  for (int t : chose) write(t), quad; Enter;
  fflush(stdout);
  int ret; read(ret); 
  ma[chose] = ret; return ret;
}

接下来就按照生成树的角度进行思考。
首先我们需要并查集,这个非常简单不在累述,然后我们会发现要进行 \(n-1\) 次连边操作。
对于每个连边操作,我们都要找到一个和根节点所在集合有边的点 \(p\) 然后连边。
那么怎么找到这样的点 \(p\) 呢?这里有一个显然的结论:

对于点集 \(A\) 和 \(B\) ,如果 \(A\) 和 \(B\) 中的点有边相连,那么满足 \(ask(A)+ask(B)<ask(A\cup B)\)

运用这个结论,我们就可以找到上文所讲的 \(p\) 。

我们令根节点所在的点集为 \(A\) ,其他的点构成的点集为 \(B\) 。
同时我们令上文结论中的查询方式为 \(check(A,B)\) ,及调用 \(check(A,B)\) 就可以知道是否有边。
因为询问次数控制较为严格,我们考虑 \(O(n\log n)\) 的较大常数做法。
直接能够想到的是二分做法:(假设 \(B\) 集合的大小为 \(L\))

  • 我们把 \(B\) 按照大小平均分成两个集合 \(B_1\) 和 \(B_2\) 。
  • 分别查询 \(check(B_1,A)\) 和 \(check(B_2,A)\) ,如果一个为真则取为真的,否则任意取一个。
  • 不难发现,最后集合 \(B\) 只会剩下一个节点,那个节点就是 \(p\) 。复杂度 \(O(\log n)\) 。

找到了 \(p\) ,我们还要知道 \(p\) 和根节点集合中的哪一个点有边,按照相似的方法即可。
只不过此次查询的 \(check\) 操作更为简洁,复杂度还是 \(O(\log n)\) 。

重复 \(n-1\) 次上述的操作,我们就找到了一个生成树,接下来对树染色非常简单。
我们令染为白色和黑色的点集分别为 \(white\) 和 \(black\) ,进行一次 \(check(white,black)\) 即可判断二分图。
如果是二分图,那么接下来非常简单,现在来讨论非二分图的情况。

我的做法是随机化,每一次对集合进行一次 \(\tt random_shuffle\) ,然后取 \(\frac{L}{2}\) 。
进行查询,如果可以的话让点集大小直接减半,不知道对不对,反正我过了

所以这样下来,复杂度约为 \(O(n\log n)\) 带上 \(3\sim 5\) 倍常数,可以通过。
具体可以看代码:

#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <string>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
#define quad putchar(' ')
#define Enter putchar('\n')

using std::abs;
using std::pair;
using std::string;
using std::make_pair;

// #define int long long

template <class T> void read(T &a);
template <class T> void write(T x);

template <class T, class ...rest> void read(T &a, rest &...x);
template <class T, class ...rest> void write(T x, rest ...a);

const int N = 1005;

int n, root_edge, tot, ok[N], edgetot, col[N];
int deep[N], fa[N][15], sta[N], top;
std::vector <int> now, rt, white, black;
std::vector <int> dis[N];

std::map <std::vector<int>, int> ma;

struct Edge {
  int x, y;
  Edge (int _x = 0, int _y = 0) {x = _x; y = _y;}
} edge[N * N];
namespace UFST {
int fa[N], siz[N];
inline int find(int);
inline void rebuild(int);
inline void merge(int, int);
}

inline int ask(std::vector<int> chose) {
  if (chose.size() == 1) return 0;
  std::sort(chose.begin(), chose.end());
  if (ma[chose]) return ma[chose];
  printf("? ");
  write(chose.size()), Enter;
  for (int t : chose) write(t), quad; Enter;
  fflush(stdout);
  int ret; read(ret); 
  ma[chose] = ret; return ret;
}
inline bool check(int l, int r) {
  now.clear();
  for (int i = l; i <= r; i++) now.emplace_back(ok[i]);
  int edge1 = ask(now);
  for (int t : rt) now.emplace_back(t);
  int edge2 = ask(now);
  if (edge1 + root_edge < edge2) return true;
  return false;
}
inline bool check2(int l, int r, int p) {
  now.clear();
  for (int i = l; i <= r; i++) now.emplace_back(ok[i]);
  int edge1 = ask(now);
  now.emplace_back(p);
  int edge2 = ask(now);
  if (edge1 < edge2) return true;
  return false;
}

inline void DFS(int, int);
inline int LCA(int, int);

signed main(void) {
  read(n); UFST::rebuild(n);
  if (n == 1) {
    printf("Y 1 \n1");
    return 0;
  }
  now.emplace_back(1); 
  root_edge = ask(now);
  rt = now;
  for (int edgenum = 1, rootteam; edgenum < n; edgenum++) {
    root_edge = ask(rt);
    tot = 0, rootteam = UFST::find(1);
    for (int i = 1; i <= n; i++)
      if (UFST::find(i) != rootteam) ok[++tot] = i;
    int left = 1, right = tot, mid;
    while (left < right) {
      mid = (left + right) / 2;
      if (check(left, mid)) right = mid;
      else left = mid + 1; 
    }
    int point = ok[left];
    tot = 0;
    for (int t : rt) ok[++tot] = t;
    left = 1; right = tot;
    while (left < right) {
      mid = (left + right) / 2;
      if (check2(left, mid, point)) right = mid;
      else left = mid + 1;
    }
    UFST::merge(ok[left], point);
    edge[++edgetot] = Edge(ok[left], point);
    rt.emplace_back(point);
  }
  for (int i = 1; i <= edgetot; i++) {
    Edge p = edge[i];
    dis[p.x].emplace_back(p.y);
    dis[p.y].emplace_back(p.x);
  }
  DFS(1, 0);
  for (int i = 1; i <= n; i++) {
    if (col[i] == 0) white.emplace_back(i);
    else black.emplace_back(i);
  }
  // for (int num : white) write(num), quad; Enter;
  // for (int num : black) write(num), quad; Enter;
  int edge1 = ask(white), edge2 = ask(black);
  // printf("!!!");write(white.size(), edge1);
  int p1, p2;
  if (edge1 == 0 && edge2 == 0) {
    printf("Y "); write(white.size()), Enter;
    for (int num : white) write(num), quad; Enter;
    return 0;
  } else if (edge1 != 0) {
    tot = 0;
    for (int num : white) ok[++tot] = num;
    while (1) {
      now.clear();
      std::random_shuffle(ok + 1, ok + 1 + tot);
      for (int i = 1; i * 2 - 1 <= std::max(tot, 3); i++) now.emplace_back(ok[i]);
      // for (int t : now) write(t), quad; Enter; write(ask(now)); Enter;
      if (ask(now)) { 
        if (now.size() == 2) {p1 = ok[1]; p2 = ok[2]; break;}
        tot = (tot + 1) / 2;
      }
    }
  } else if (edge2 != 0) {
    tot = 0;
    for (int num : black) ok[++tot] = num;
    while (1) {
      now.clear();
      std::random_shuffle(ok + 1, ok + 1 + tot);
      for (int i = 1; i * 2 - 1 <= tot; i++) now.emplace_back(ok[i]);
      if (ask(now)) { 
        if (now.size() == 2) {p1 = ok[1]; p2 = ok[2]; break;}
        tot = (tot + 1) / 2;
      }
    }
  }
  printf("N "); 
  int lca = LCA(p1, p2);
  write(deep[p1] + deep[p2] - 2 * deep[lca] + 1); Enter;
  while (1) {
    write(p1), quad;
    p1 = fa[p1][0];
    if (p1 == lca) break;
  } 
  while (1) {
    sta[++top] = p2;
    if (p2 == lca) break;
    p2 = fa[p2][0];
  }
  while (top) write(sta[top]), quad, top --; Enter;
  return 0;
}

inline void DFS(int now, int father) {
  deep[now] = deep[father] + 1;
  col[now] = 1 - col[father];
  for (int i = 0; i <= 12; i++) fa[now][i + 1] = fa[fa[now][i]][i];
  for (int t : dis[now]) {
    if (t == father) continue;
    fa[t][0] = now;
    DFS(t, now);
  }
}
inline int LCA(int x, int y) {
  if (deep[x] < deep[y]) std::swap(x, y);
  for (int i = 13; i >= 0; i--)
    if (deep[fa[x][i]] >= deep[y]) x = fa[x][i];
  if (x == y) return x;
  for (int i = 13; i >= 0; i--)
    if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
  return fa[x][0];
}

namespace UFST {
inline int find(int x) {
  return x == fa[x] ? x : fa[x] = find(fa[x]);
}
inline void rebuild(int n) {
  for (int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1;
}
inline void merge(int x, int y) {
  x = find(x); y = find(y);
  if (x == y) return ;
  if (siz[x] > siz[y]) std::swap(x, y);
  fa[x] = y; siz[y] += siz[x];
}
}

template <class T> void read(T &a) {
  int s = 0, t = 1;
  char c = getchar();
  while (!isdigit(c) && c != '-') c = getchar();
  if (c == '-') c = getchar(), t = -1;
  while (isdigit(c)) s = s * 10 + c - '0', c = getchar();
  a = s * t;
}
template <class T> void write(T x) {
  if (x == 0) putchar('0');
  if (x < 0) putchar('-'), x = -x;
  int top = 0, sta[50] = {0};
  while (x) sta[++top] = x % 10, x /= 10;
  while (top) putchar(sta[top] + '0'), top --;
  return ;
}

template <class T, class ...rest> void read(T &a, rest &...x) {
  read(a); read(x...);
}
template <class T, class ...rest> void write(T x, rest ...a) {
  write(x); quad; write(a...);
}

``

标签:std,chose,fa,CF1033E,void,int,体验,include
来源: https://www.cnblogs.com/Oier-GGG/p/16496384.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有