11-24 & 11-25 省选集训总结/题解

//题目来源:ICPCcamp2017及其它

HDRF

Description

给出一棵 n 个节点的有根树,节点 1 是根。每个节点有一个权值。进行如下操作:

  • 从根节点出发
  • 朝着当前节点子树中(不包括当前节点)最小权值点走一步
  • 若当前节点是叶子,将这个节点删除,回到根节点

求删除的节点序列
n105

Solution

发现走到一个最小节点 u 时,在不删除完 u 的所有子树之前是不会走到其它节点的。这样就变成了子问题, dfs + 线段树维护最小值即可。

#include<bits/stdc++.h>
using namespace std;

#define N 100001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drp(i, a, b) for (int i = a; i >= b; i--)
#define fech(i, x) for (int i = 0; i < x.size(); i++)
#define INF 2100000000

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

inline void write(int x) {
    if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
    char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
}

int n, w[N];
vector<int> g[N];
int ind, dfq[N], getin[N], leave[N];
int mn[N << 2];
#define ls rt << 1
#define rs rt << 1 | 1
#define lson ls, l, l + r >> 1
#define rson rs, (l + r >> 1) + 1, r

inline void pushup(int rt) { mn[rt] = w[mn[ls]] < w[mn[rs]] ? mn[ls] : mn[rs]; }

void build(int rt, int l, int r) { if (!(l ^ r)) { mn[rt] = dfq[l]; return; } build(lson), build(rson), pushup(rt); }

void update(int rt, int l, int r, int pos) {
    if (!(l ^ r)) { w[dfq[pos]] = INF; return; }
    if (pos <= ((l + r) >> 1)) update(lson, pos); else update(rson, pos);
    pushup(rt);
}

int query(int rt, int l, int r, int L, int R) {
    if (l >= L && r <= R) return mn[rt];
    int mid = l + r >> 1, ans1 = 0, ans2 = 0;
    if (L <= mid) ans1 = query(lson, L, R);
    if (R > mid) ans2 = query(rson, L, R);
    return w[ans1] < w[ans2] ? ans1 : ans2;
}

#define output(u) write(u), putchar(' '), update(1, 1, n, getin[u])
void solve(int u) {
    while (1) {
        int v = query(1, 1, n, getin[u] + 1, leave[u]);
        if (getin[u] == leave[u] || w[v] == INF) { output(u); return; }
        solve(v);
    }
}

void dfs(int u, int f) { dfq[getin[u] = ++ind] = u; fech(i, g[u]) if (g[u][i] ^ f) dfs(g[u][i], u); leave[u] = ind; }
inline void addEdge(int u, int v) { g[u].push_back(v); g[v].push_back(u); }
int main() {
    n = read(); rep(i, 1, n) w[i] = read(); rep(i, 2, n) addEdge(read(), read());
    w[0] = 0x7fffffff; dfs(1, 0); build(1, 1, n); solve(1);
    return 0;
}

Shortest Path Queries

Description

给出一个 HW 的矩阵,每一个位置上包含一个非负的整数。一条路径长度定义为路径所经过的位置的数的和。
给出 Q 个询问,每次询问从一个位置到另一个位置的最短路径的长度。
H10,W104,Q105

Solution

线段树维护一些列里面所有点到其他列所有点的最短路。递归处理。
正在考虑实现。


子树直径

Description

Y 有一棵 n 个节点的树,每条边都有正的边权。
J q 个询问,每次小 J 会删掉这个树中的k条边,这棵树被分成 k+1 个连通块。小J想知道每个连通块中最远点对距离的和。
这里的询问是互相独立的,即每次都是在小 Y 的原树上进行操作。
n,q,k105

Solution

两棵树合并时,新树直径的两个端点必然来自原来两树的直径端点。
所以 dfs + 线段树维护子树直径和直径端点。

#include<bits/stdc++.h>
using namespace std;

#define N 100001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drp(i, a, b) for (int i = a; i >= b; i--)
#define fech(i, x) for (int i = 0; i < x.size(); i++)

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

inline void write(int x) {
    if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
    char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
}

int n;
struct edgeType { int u, v, w; }eg[N]; int tot;
vector<int> g[N];
int dep[N], fa[N][17], dis[N];
int dfq[N], getin[N], leave[N], ind;
struct segType { int d, a, b; bool operator < (const segType& t) const { return d < t.d; } }seg[N << 2];

#define ls rt << 1
#define rs ls | 1
#define mid (l + r >> 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r

int lca(int x, int y) {
}

inline int getDis(int u, int v) { return dis[u] + dis[v] - (dis[lca(u, v)] << 1); }

inline segType choose(segType x, segType y) {
    if (x.d == -1) return y; if (y.d == -1) return x;
    segType res1 = segType{ getDis(x.a, y.a), x.a, y.a };
    segType res2 = segType{ getDis(x.a, y.b), x.a, y.b };
    segType res3 = segType{ getDis(x.b, y.a), x.b, y.a };
    segType res4 = segType{ getDis(x.b, y.b), x.b, y.b };
    //  return max({ x, y, res1, res2, res3, res4 });
    return max(x, max(y, max(res1, max(res2, max(res3, res4)))));
}

void build(int rt, int l, int r) {
    if (!(l ^ r)) { seg[rt] = segType{ 0, dfq[l], dfq[l] }; return; }
    build(lson), build(rson); seg[rt] = choose(seg[ls], seg[rs]);
}

void dfs(int u, int f) {
    dfq[getin[u] = ++ind] = u;
    fa[u][0] = f;
    rep(i, 1, 17) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    fech(i, g[u]) {
        edgeType e = eg[g[u][i]]; if (e.v == f) continue;
        dis[e.v] = dis[u] + e.w; dep[e.v] = dep[u] + 1; dfs(e.v, u);
    }
    leave[u] = ind;
}

segType query(int rt, int l, int r, int L, int R) {
    if (l >= L && r <= R) return seg[rt];
    segType res = segType{ -1, 0, 0 };
    if (L <= mid) res = choose(res, query(lson, L, R));
    if (R > mid) res = choose(res, query(rson, L, R));
    return res;
}

int x[N], s[N];
vector<int> edge[N];
int ans;
bool cmp(int a, int b) { return getin[a] < getin[b]; }

void solve(int u) {
    segType res = segType{ -1, x[u], x[u] };
    int L = getin[x[u]], R = leave[x[u]];
    for (int i = 0; i < edge[u].size(); i++) {
        int v = edge[u][i]; solve(v);
        res = choose(res, query(1, 1, n, L, getin[x[v]] - 1));
        L = leave[x[v]] + 1;
    }
    ans += choose(res, query(1, 1, n, L, R)).d;
}

int main() {
    n = read(); rep(i, 2, n) {
        int u = read(), v = read(), w = read();
        eg[++tot] = edgeType{ u, v, w }; g[u].push_back(tot);
        eg[++tot] = edgeType{ v, u, w }; g[v].push_back(tot);
    }
    dfs(1, 0); build(1, 1, n);
    int q = read(); puts("");
    printf("%d\n", lca(3, 5));
    while (q--) {
        int k = read();
        rep(i, 1, k) {
            edgeType e = eg[(read() << 1) - 1];
            x[i] = dep[e.u] > dep[e.v] ? e.u : e.v;
        }
        sort(x + 1, x + 1 + k, cmp);
        x[0] = 1; s[1] = 0; int t = 1;
        rep(i, 0, k) edge[i].clear();
        rep(i, 1, k) {
            while (!(getin[x[s[t]]] <= getin[x[i]] && leave[x[i]] <= leave[x[s[t]]])) t--;
            edge[s[t]].push_back(i);
            s[++t] = i;
        }
        ans = 0; solve(0); write(ans); puts("");
    }
    return 0;
}

Common Ancestor

Description

给出两棵包含 n 个节点的树,标号都是 1n ,根节点都是 1 号节点,并且保证从任意一个节点到根的路径上,标号都是递减的。
现给出q个询问,每次询问从第一棵树的 x 节点到根和从第二棵树的 y 节点到根的路径上,公共节点中标号最大的是谁。
n,q105

Solution

考虑在链上做的情况,显然有贡献的是一个矩形,可以用数据结构维护。在树上就同理,书上主席树。


Coprime Queries

Description

给出一个序列 a1,a2,,an ,你需要回答 q 个询问 (l,r,x),表示要找到一个最大的 p ,要求 lpr,且 ap x 是互素的。
n,q100000,1x,ai100000

Solution

质因数分解 x ,对因数做区间询问倍数,然后容斥出有多少个不和 x 互素的。实际操作可以整体二分。


动态背包

Description

初始时有 n 件物品,你有一个大小为 k 的背包。现在有 q 个操作:

  • 1vw:添加一个价值为 v ,大小为 w 的物品

    • 2x :删除第 x 件物品
    • 3:询问用背包能装下的物品的最大价值和
    • n5000,k1000,q30000

      Solution

      每次加入一个物品的更新答案,删除撤销。可持久化数据结构。


      奇数度数图

      Description

      给出一张 n 个点初始时没有边的图。按顺序依次加入 m 条边,每条边有一个边权。当加入第 i 条边时,你需要从前 i 条边中选出一个子集,使得每个点的度数都为奇数。你需要让选出的边集中权值最大的边的权值尽量小,输出这个权值。

      Solution

      奇数大小的连通块一定不合法。一个合法的图可以通过不断删除简单环并保证它仍然合法。


      Matrix Recurrence

      Description

      Solution

      双栈。当要撤销时,把右栈倒序插入左栈。前缀积。


      Fold

      Description

      你有一个长度为 n 的纸条,接下来你会进行 m 个操作:

      • 1 p:把长度为 p 的左半段向右折叠
      • 2 l r:询问在 [l,r] 这个区间中纸条总长度

      n,m105

      Solution

      区间查询当然用线段树,关键是修改。发现收敛很快,所以直接暴力在线段树上更新。然而如果是这样:
      这里写图片描述
      复杂度就挂了。那么我们记两个指针,表示左右端点实际在哪,在出现这种情况的时候交换两个指针就可以了。
      当然具体细节很麻烦……

      #include<bits/stdc++.h>
      using namespace std;
      
      #define N 100001
      #define rep(i, a, b) for (int i = a; i <= b; i++)
      
      inline int read() {
          int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
          while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
      }
      
      inline void write(int x) {
          if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
          char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
      }
      
      int n;
      int lft = 1, rig;
      
      int sum[N << 2];
      #define ls rt << 1
      #define rs ls | 1
      #define mid (l + r >> 1)
      #define lson ls, l, mid
      #define rson rs, mid + 1, r
      #define pushup(rt) sum[rt] = sum[ls] + sum[rs]
      void build(int rt, int l, int r) { if (!(l ^ r)) { sum[rt] = 1; return; } build(lson), build(rson), pushup(rt); }
      void update(int rt, int l, int r, int pos, int val) {
          if (!(l ^ r)) { sum[rt] += val; return; } 
          if (pos <= mid) update(lson, pos, val); else update(rson, pos, val); pushup(rt);
      }
      int query(int rt, int l, int r, int L, int R) {
          if (l >= L && r <= R) return sum[rt];
          int ans = 0; if (L <= mid) ans += query(lson, L, R); if (R > mid) ans += query(rson, L, R); return ans;
      }
      
      int main() {
          n = rig = read(); build(1, 1, n); int m = read(); while (m--) {
              int op = read(); 
              if (op ^ 2) {
                  int x = read();
                  if (lft <= rig)
                      if (x <= ((rig - lft + 1) >> 1)) {
                          int p = lft + x - 1; lft = p + 1;
                          rep(i, 1, x) update(1, 1, n, p + i, query(1, 1, n, p - i + 1, p - i + 1));
                      }       
                      else {
                          int p = rig - x + 1; rig = p - 1; swap(lft, rig);
                          rep(i, 1, x) update(1, 1, n, p - i, query(1, 1, n, p + i - 1, p + i - 1));
                      }
                  else 
                      if (x <= ((lft - rig + 1) >> 1)) {
                          int p = lft - x + 1; lft = p - 1; 
                          rep(i, 1, x) update(1, 1, n, p - i, query(1, 1, n, p + i - 1, p + i - 1));
                      }
                      else {
                          int p = rig + x - 1; rig = p + 1; swap(rig, lft);
                          rep(i, 1, x) update(1, 1, n, p + i, query(1, 1, n, p - i + 1, p - i + 1));
                      }
              }
              else {
                  int l = read(), r = read(); if (rig >= lft) l += lft - 1, r += lft - 1; else l = lft - l + 1, r = lft - r + 1;
                  write(query(1, 1, n, l, r)); puts("");
              }
          }
          return 0;
      }

      Paper

      Description

      Solution

      二分。主席树维护向左延伸、向右延伸。


      Median on Binary Tree

      Description

      Solution

      这个题有单调性,于是可以二分。每次加入点,在链上改。


      Independent Events

      Description

      Solution

      用泰勒展开化简一下式子,然后就是一个简单的线段树。
      但是我还不会泰勒展开。


      Welcome to ICPCcamp 2017

      Description

      Solution

      一个一个加入答案,然后就用一个奇妙的 BIT 维护


      Lowest Common Ancestor

      Description

      Solution

      每个点的贡献就是在他之前的点被走了几次。然后在链上的话就是我最爱的链剖啦!再加一个差分就好了。


      Longest Path

      Description

      Solution

      点分治。然后线段树查询。


      LYKMUL

      Description

      Solution

      其实就是交乘上并。 (A,B) 的贡献是 2y2yx y 是一定含A可以包含 B 的,x 是都包含的。线段树维护,删尾加头。


      Subsequence Count

      Description

      Solution

      先列出转移方程,发现可以用矩阵快速幂优化。然后发现就是交换矩阵一二行、一二列。有一个结论:乘出来以后再交换等价于先交换再乘。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值