「多校训练」2021牛客G7-F. xay loves trees

xay loves trees

线段树、思维暴力、树上问题

题意

此题 的改版,区别是要求第一颗树选点连续。

思路

  • 容易想到点集在第一课树上是一条链,然后根据第二颗树的 dfs 序关系来判断是否可选点,用线段树染色来做。
  • 而选点是否冲突的依据是染色有无冲突,即最大染色值是否 > 2。
  • 如果发生冲突考虑从链顶部开始撤销操作,每次撤销后恢复复杂度会被链加菊花卡炸,考虑优化。
  • 每次冲突时,只从顶部删除一个,依然冲突就不记录现在链的答案,但依然保存链的节点个数。
  • 因此每搜索到一个点,最多进行 4 次线段树操作,时间复杂度 \(O(nlogn)\)

证明正确性:若当前链出现非法,则新点集大小=原点集大小+1(加入新节点)-1(删除上端点)=原点集大小, 即新出现的非法情况的点集大小与原合法点集的大小是相同的,当最优解(即点集最大大小)更新时,肯定 在某处形成了合法解,因为只有合法解省去了-1(删除上端点)一步,才可能使得点集大小增加。 —— 陈奕翔 新昌县实验中学

const int N = 3e5 + 10;
vector<int> edges[2][N];
int ans = 0;

struct SegmentTree {
   #define ls u << 1
   #define rs u << 1 | 1
   struct T {
       int l, r;
       ll v;
       ll add;
   }tr[N << 2];
   void pushup(int u) {
       tr[u].v = max(tr[ls].v, tr[rs].v);
   }
   void update(T& rt, int add)  {
       rt.add += add, rt.v += add;
   }
   void pushdown(int u) {
       if (tr[u].add) {
           update(tr[ls], tr[u].add);
           update(tr[rs], tr[u].add);
           tr[u].add = 0;
       }
   }
   void build(int u, int l, int r) {
       tr[u].l = l, tr[u].r = r, tr[u].v = tr[u].add = 0;
       if (tr[u].l == tr[u].r) {
           return ;
       }
       int mid = (tr[u].l + tr[u].r) >> 1;
       build(ls, l, mid), build(rs, mid + 1, r);
       pushup(u);
       return ;
   }
   void modify(int u, int l, int r, int v) {
       if (tr[u].l >= l && tr[u].r <= r) {
           update(tr[u], v);
           return ;
       }
       pushdown(u);
       int mid = (tr[u].l + tr[u].r) >> 1;
       if (l <= mid) modify(ls, l, r, v);
       if (r > mid) modify(rs, l, r, v);
       pushup(u);
       return ;
   }
   int query(int u, int l, int r) {
       if (tr[u].l >= l && tr[u].r <= r) {
           return tr[u].v;
       }
       pushdown(u);
       int mid = (tr[u].l + tr[u].r) >> 1;
       int res = 0;
       if (l <= mid) res = query(ls, l, r);
       if (r > mid) res = max(res, query(rs, l, r));
       return res;
   }
} tr;
int dfn[N], tot, rk[N], sz[N], fa[N];

void dfs(int u, int p) {
   sz[u] = 1;
   dfn[u] = ++tot, rk[tot] = u;
   for (auto v: edges[1][u]) {
       if (v == p) continue;
       fa[v] = u;
       dfs(v, u);
       sz[u] += sz[v];
   }
}
vector<int> path;
bool st[N];
int n;

void dfs1(int u, int p, int tp, int cnt) {
   path.pb(u);
   vector<int> tmp;
   bool ok = true;
   tr.modify(1, dfn[u], dfn[u] + sz[u] - 1, 1);
   if (tr.query(1, 1, n) > 1) {
       tr.modify(1, dfn[path[tp]], dfn[path[tp]] + sz[path[tp]] - 1, -1);
       tmp.pb(path[tp]);
       tp++;
       cnt--;
       if (tr.query(1, 1, n) > 1) {
           ok = false;
       }
   }
   if (ok) {
       ans = max(ans, cnt);
   }
   for (auto v: edges[0][u]) {
       if (v == p) continue;
       dfs1(v, u, tp, cnt + 1);
   }
   tr.modify(1, dfn[u], dfn[u] + sz[u] - 1, -1);
   for (auto t: tmp) {
       tp--;
       tr.modify(1, dfn[t], dfn[t] + sz[t] - 1, 1);
   }
   path.pop_back();
   return;
}


int main() {
   int T;
   re(T);
   while (T--) {
       tot = ans = 0;
       re(n);
       path.clear();
       for (int i = 1; i <= n; i++) st[i] = false;
       for (int j = 0; j < 2; j++)
           for (int i = 1; i <= n; i++) edges[j][i].clear();
       for (int j = 0; j < 2; j++) {
           for (int i = 1; i < n; i++) {
               int a, b;
               re(a), re(b);
               edges[j][a].pb(b), edges[j][b].pb(a);
           }
       }
       tr.build(1, 1, n);
       dfs(1, 0);
       dfs1(1, 0, 0, 1);
       printf("%d\n", ans);
   }
   return 0;
}
posted @ 2022-10-11 10:04  Roshin  阅读(29)  评论(0编辑  收藏  举报
-->