[LDUoj 倍增] 题解(上)

简介: 细节的地方慢慢补充,欢迎提出问题,私聊/留言均可A. 跳跳棋较难B. 聚会板子题C. 祖孙询问板子题D. Dis板子题E. 次小生成树(严格次小生成树)难

细节的地方慢慢补充,欢迎提出问题,私聊/留言均可


A. 跳跳棋


96dbebe2a1814a57b287a706d0768c8f.png


较难


struct node
{
    int a, b, c;
    friend bool operator != (node a, node b)
    {
        if(a.a != b.a || a.b != b.b || a.c != b.c) return true;
        return false;
    }
    void srt()
    {
        int temp[] = {a, b, c};
        sort(temp, temp + 3);
        a = temp[0], b = temp[1], c = temp[2];
    }
} pnt1, pnt2;
node getRoot(node pnt, int &cnt)
{
    /// dis1 == dis2  -> return pnt ok;
    while(pnt.c - pnt.b != pnt.b - pnt.a)
    {
        int dis1 = pnt.b - pnt.a;
        int dis2 = pnt.c - pnt.b;
        if(dis1 < dis2)
        {
            int t = dis2 / dis1;
            if(dis2 % dis1 == 0) -- t;
            pnt.a += t * dis1;
            pnt.b += t * dis1;
            cnt += t;
        }
        else
        {
            int t = dis1 / dis2;
            if(dis1 % dis2 == 0) -- t;
            pnt.c -= t * dis2;
            pnt.b -= t * dis2;
            cnt +=  t;
        }
    }
    return pnt;
}
node moveUp(node pnt, int cnt)
{
    while(pnt.b - pnt.a != pnt.c - pnt.b && cnt)
    {
        int dis1 = pnt.b - pnt.a;
        int dis2 = pnt.c - pnt.b;
        if(dis1 < dis2)
        {
            int t = dis2 / dis1;
            if(dis2 % dis1 == 0) -- t;
            if(cnt < t) t = cnt;
            pnt.a += t * dis1;
            pnt.b += t * dis1;
            cnt -= t;
        }
        else
        {
            int t = dis1 / dis2;
            if(dis1 % dis2 == 0) -- t;
            if(cnt < t) t = cnt;
            pnt.c -= t * dis2;
            pnt.b -= t * dis2;
            cnt -= t;
        }
    }
    return pnt;
}
int main()
{
    cin >> pnt1.a >> pnt1.b >> pnt1.c;
    cin >> pnt2.a >> pnt2.b >> pnt2.c;
    pnt1.srt(), pnt2.srt();
    int cnt1 = 0, cnt2 = 0;
    node root1 = getRoot(pnt1, cnt1);
    node root2 = getRoot(pnt2, cnt2);
    if(root1 != root2)
    {
        puts("NO");
        return 0;
    }
    // puts("OK");
    if(cnt1 < cnt2) swap(pnt1, pnt2), swap(cnt1, cnt2);
    int l = 0, r = 1000000000;
    pnt1 = moveUp(pnt1, cnt1 - cnt2);
    int ans = 0;
    // puts("OK");
    while(l <= r)
    {
        int mid = l + r >> 1;
        // cout << l << "   " << r <<endl;
        if(!(moveUp(pnt1, mid) != moveUp(pnt2, mid)))
        {
            r = mid - 1;
            ans = mid;
        }
        else l = mid + 1;
        // puts("end of while");
    }
    puts("YES");
    cout << ans * 2 + cnt1 - cnt2 << endl;
    return 0;
}
/**
**/


B. 聚会


8c62aa67255143c88c968e61be1f0eaa.png


板子题


n个城市之间的距离是1

所以在求lca的过程中,深度的差值即可表示为距离

两个点a,b,之间的lca为_lca

那么这两个点之间的距离为dep[a] - dep[_lca] + dep[b] - dep[_lca],-> dep[a] + dep[b] - dep[_lca] * 2

这仅仅是两个点的情况

三个点的情况略有不同:

若三个点在同一棵子树上:


若三个点在同一条链上:


所以说我们要考虑任意两点之间的关系,然后求两次lca,第一次的lca即为可能的答案位置,最终答案要考虑所有情况中花费最小的那个


int n,m;
int root;
int head[maxn];
struct node {
  int u;
  int v;
  int w;
  int next;
} e[maxn];
int dep[maxn];/// save the depth of every node
int fa[maxn][30];
int lg[maxn];
int cnt = 0;
void init() {
  memset(head,-1,sizeof head);
}
void add(int u,int v) {
  e[cnt].u = u;
  e[cnt].v = v;
  e[cnt].next = head[u];
  head[u] = cnt ++;
}
void dfs(int cur,int rt) {
  fa[cur][0] = rt,dep[cur] = dep[rt] + 1;/// the depth of the current node changed
  for(int i=1; i <= lg[dep[cur]]; i++) {
    fa[cur][i] = fa[fa[cur][i-1]][i-1];
  }
  for(int i=head[cur]; ~i; i = e[i].next) { /// visit all linked nodes
    if(e[i].v != rt) dfs(e[i].v,cur);
  }
}
void cal() {
  for(int i=1; i<=n; i++) {
    lg[i] = lg[i-1] + (1 << lg[i-1] == i);/// 2 ^ lg[i-1] == i true + 1
  }
}
int lca(int x,int y) {
  if(dep[x] < dep[y]) swap(x,y);
  while(dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
  if(x == y) return y;
  /// big -> small
  for(int k = lg[dep[x]] - 1; k >= 0; k --) {
    if(fa[x][k] != fa[y][k]) {
      x = fa[x][k];
      y = fa[y][k];
    }
  }
  return fa[x][0];
}
int main() {
  cin >> n >> m;
  cal();
  init();
  for(int i=1; i<n; i++) {
    int u = read,v = read;
    add(u,v);
    add(v,u);
  }
  ll ans = inf,dis;
  int pos = 0;
  int lca1,lca2;
  dfs(1,0);
  for(int i=1; i<=m; i++) {
    ans = inf;
    int x = read,y = read,z = read;
    lca1  = lca(x,y);
    dis = dep[x] + dep[y] - 2 * dep[lca1];
    lca2 = lca(lca1,z);
    dis += dep[lca1] + dep[z] - 2 * dep[lca2];
    if(dis < ans) {
      ans = dis;
      pos = lca1;
    }
//    debug(lca1);
//    debug(dis);
    swap(x,z);
    lca1 = lca(x,y);
    dis = dep[x] + dep[y] - 2 * dep[lca1];
    lca2 = lca(lca1,z);
    dis += dep[lca1] + dep[z] - 2 * dep[lca2];
    if(dis < ans) {
      ans = dis;
      pos = lca1;
    }
    swap(y,z);
    lca1 = lca(x,y);
    dis = dep[x] + dep[y] - 2 * dep[lca1];
    lca2 = lca(lca1,z);
    dis += dep[lca1] + dep[z] - 2 * dep[lca2];
    if(dis <ans) {
      ans = dis;
      pos = lca1;
    }
    printf("%d %lld\n",pos,ans);
  }
  return 0;
}
/**
6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
6 2
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
**/


C. 祖孙询问


cbe7f4e5d73b4a8da29c0b24527ff209.png



板子题


求两点之间的lca,如果lca与其中一点相同,输出对应的值,反之输出0

int n,m;
int root;
int head[maxn];
struct node{
    int u;
    int v;
    int next;
}e[maxn];
int dep[maxn];/// save the depth of every node
int fa[maxn][30];
int lg[maxn];
int cnt = 0;
void init(){
    memset(head,-1,sizeof head);
}
void add(int u,int v){
    e[cnt].u = u;
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt ++;
}
void dfs(int cur,int rt){
    fa[cur][0] = rt,dep[cur] = dep[rt] + 1;/// the depth of the current node changed
    for(int i=1;i <= lg[dep[cur]];i++){
        fa[cur][i] = fa[fa[cur][i-1]][i-1];
    }
    for(int i=head[cur];~i;i = e[i].next){/// visit all linked nodes
        if(e[i].v != rt) dfs(e[i].v,cur);
    }
}
void cal(){
    for(int i=1;i<=n;i++){
        lg[i] = lg[i-1] + (1 << lg[i-1] == i);/// 2 ^ lg[i-1] == i true + 1
    }
}
int lca(int x,int y){
    if(dep[x] < dep[y]) swap(x,y);
    while(dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
    if(x == y) return y;
    /// big -> small
    for(int k = lg[dep[x]] - 1;k >= 0;k --){
        if(fa[x][k] != fa[y][k]){
            x = fa[x][k];
            y = fa[y][k];
        }
    }
    return fa[x][0];
}
int main() {
    cin >> n;
    cal();
    init();
    for(int i=1;i<=n;i++){
        int x = read,y = read;
        if(y == -1) {
          root = x;
          continue;
        }
        add(x,y);
        add(y,x);
    }
    dfs(root,0);
    cin >> m;
    for(int i=1;i<=m;i++){
        int x = read,y = read;
        int _lca = lca(x,y);
        if(_lca == x) puts("1");
        else if(_lca == y) puts("2");
        else puts("0");
    }
  return 0;
}/// ac_code
/**
**/


D. Dis


be47919f14484ab4a6de5024b10fd16a.png


板子题


考虑在更新depth的时候,顺便更新一下两点之间的距离,(在题目中如果说任意两点之间的距离为1的情况,dis可以用depth代替)

typedef pair<int,int> PII;
int n,m;
int root;
int head[maxn];
struct node {
  int u;
  int v;
  int w;
  int next;
} e[maxn];
int dep[maxn];/// save the depth of every node
int fa[maxn][30];
ll dis[maxn];
int lg[maxn];
int cnt = 0;
map<PII,int> mp;
void init() {
  memset(head,-1,sizeof head);
}
void add(int u,int v,int w) {
  e[cnt].u = u;
  e[cnt].v = v;
  e[cnt].w = w;
  e[cnt].next = head[u];
  head[u] = cnt ++;
}
void dfs(int cur,int rt) {
  fa[cur][0] = rt,dep[cur] = dep[rt] + 1;/// the depth of the current node changed
  dis[cur] = dis[rt] + mp[ {rt,cur}];
  for(int i=1; i <= lg[dep[cur]]; i++) {
    fa[cur][i] = fa[fa[cur][i-1]][i-1];
  }
  for(int i=head[cur]; ~i; i = e[i].next) { /// visit all linked nodes
    if(e[i].v != rt) dfs(e[i].v,cur);
  }
}
void cal() {
  for(int i=1; i<=n; i++) {
    lg[i] = lg[i-1] + (1 << lg[i-1] == i);/// 2 ^ lg[i-1] == i true + 1
  }
}
int lca(int x,int y) {
  if(dep[x] < dep[y]) swap(x,y);
  while(dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
  if(x == y) return y;
  /// big -> small
  for(int k = lg[dep[x]] - 1; k >= 0; k --) {
    if(fa[x][k] != fa[y][k]) {
      x = fa[x][k];
      y = fa[y][k];
    }
  }
  return fa[x][0];
}
int main() {
  cin >> n >> m;
  cal();
  init();
  for(int i=1; i<n; i++) {
    int u = read,v = read,w = read;
    mp[{u,v}] = mp[{v,u}] = w;
    add(u,v,w);
    add(v,u,w);
  }
  dfs(1,0);
  for(int i=1; i<=m; i++) {
    int x = read,y = read;
    int _lca = lca(x,y);
    ll ans = dis[x] + dis[y] - 2 * dis[_lca];
    printf("%lld\n",ans);
  }
  return 0;
}
/**
2 2 
1 2 100 
1 2 
2 1
**/


E. 次小生成树(严格次小生成树)


018eb097ff394447a3fcf1445365fe61.png



切掉这个题需要掌握:dfs技巧 + lca + 倍增 + 维护次小边权 + 最小生成树

这个题写下来之后会有很大提高,而且会改掉写代码开内存的坏习惯

7a50b5871eea475a9358a6ac3463e851.png


int n, m;
struct node {
  ll u, v, w;
  ll nex;
} e[maxn << 1];
int cnt;
int head[maxn << 1];
void init() {
  cnt = 0;
  for (int i = 1; i <= 2 * n; i++) {
    head[i] = -1;
  }
}
void add(ll u, ll v, ll w) {
  e[cnt].u = u;
  e[cnt].v = v;
  e[cnt].w = w;
  e[cnt].nex = head[u];
  head[u] = cnt ++;
}
ll bz[100007][20],ma[100007][20],mi[100007][20],depth[100007];
void dfs(ll u, ll fa) {
  bz[u][0] = fa;
  for (int i = head[u]; ~i; i = e[i].nex) {
    ll to = e[i].v;
    if (to == fa) continue;
    depth[to] = depth[u] + 1L;///depth inc
    ma[to][0] = e[i].w;
    mi[to][0] = -999999999999999;
    dfs(to, u);
  }
}
ll lca(ll x, ll y) {
  if (depth[x] < depth[y])
    swap(x, y);
  for (ll i = 18; i >= 0; i--) {
    if (depth[bz[x][i]] >= depth[y])
      x = bz[x][i];
  }
  if (x == y)
    return x;
  for (ll i = 18; i >= 0; --i) {
    if (bz[x][i] != bz[y][i]) {
      x = bz[x][i];
      y = bz[y][i];
    }
  }
  return bz[x][0];
}
int fa[maxn];
void fainit() {
  for (int i = 1; i <= 2 * n; i++)
    fa[i] = i;
}
ll find(ll x) {
  if (x == fa[x])
    return x;
  else
    return fa[x] = find(fa[x]);
}
node a[maxn];
bool cmp(node a, node b) {
  return a.w < b.w;
}
bool vis[maxn];
void cal() {
  for (ll i = 1; i <= 18; i++) {
    for (ll j = 1; j <= n; j++) {
      bz[j][i] = bz[bz[j][i - 1]][i - 1];
      ma[j][i] = max(ma[j][i - 1], ma[bz[j][i - 1]][i - 1]);
      mi[j][i] = max(mi[j][i - 1], mi[bz[j][i - 1]][i - 1]);
      if (ma[j][i - 1] > ma[bz[j][i - 1]][i - 1])
        mi[j][i] = max(mi[j][i], ma[bz[j][i - 1]][i - 1]);
      else if (ma[j][i - 1] < ma[bz[j][i - 1]][i - 1])
        mi[j][i] = max(mi[j][i], ma[j][i - 1]);
    }
  }
}
ll get(int u, int v, ll w) {
  ll ret = -inf;
  for (int i = 18; i >= 0; i--) {
    if (depth[bz[u][i]] >= depth[v]) {
      if (w != ma[u][i])
        ret = max(ret, ma[u][i]);
      else
        ret = max(ret, mi[u][i]);
      u = bz[u][i];
    }
  }
  return ret;
}
int main() {
  n = read,m = read;
  init();
  for(int i=1; i<=m; i++) {
    a[i].u = read,a[i].v = read,a[i].w = read;
  }
  fainit();
  sort(a+1,a+1+m,cmp);
  ll tot = 0;
  for(int i=1; i<=m; i++) {
    int fau = find(a[i].u);
    int fav = find(a[i].v);
    if(fau == fav) continue;
    tot += a[i].w;
    fa[fau] = fav;
    add(a[i].u,a[i].v,a[i].w);
    add(a[i].v,a[i].u,a[i].w);
    vis[i] = true;///used
  }
//  cout << tot <<endl;
  mi[1][0] = -inf;
  depth[1] = 0;
  dfs(1,-1);
  cal();
  ll ret = inf;
  for(int i=1; i<=m; i++) {
    if(!vis[i]) {
      int u = a[i].u;
      int v = a[i].v;
      ll w = a[i].w;
      int _lca = lca(u,v);
      ll maxx = get(u,_lca,w);
      ll mini = get(v,_lca,w);
      ret = min(ret,tot-max(maxx,mini)+w);
    }
  }
  cout << ret <<endl;
  return 0;
}
/**
**/



目录
相关文章
|
算法 Android开发 索引
LeetCode 周赛上分之旅 #44 同余前缀和问题与经典倍增 LCA 算法
学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。
81 0
|
5月前
【洛谷 P1002】[NOIP2002 普及组] 过河卒 题解(递归+记忆化搜索)
`NOIP2002`普及组的过河卒问题是一个棋盘路径计数挑战。卒从$(0,0)$出发到$(n,m)$,只能向下或向右移动,马在$(c1,c2)$固定,控制某些点。任务是计算不受马阻挡的路径数。输入是目标和马的位置,输出是路径总数。使用动态规划和记忆化搜索避免重复计算,样例输入$(6,6,3,3)$输出$6$。代码中定义了$f(x,y)$计算$(x,y)$处的路径数,利用边界条件和递推关系计算。
69 0
|
5月前
|
存储 算法 数据挖掘
力扣18题 四数之和:从基础到精通,一篇文章带你突破算法难关
力扣18题 四数之和:从基础到精通,一篇文章带你突破算法难关
|
人工智能 算法 C++
洛谷 P1090 合并果子(贪心)
洛谷 P1090 合并果子(贪心)
133 0
|
算法 Java 测试技术
LeetCode 周赛上分之旅 #46 经典二分答案与质因数分解
学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。
66 0
LeetCode 周赛上分之旅 #46 经典二分答案与质因数分解
|
算法
代码随想录Day28 贪心03 LeetCode T1005 K次取反后最大化的数组和 LeetCode T134 加油站 LeetCode T135 分发糖果
代码随想录Day28 贪心03 LeetCode T1005 K次取反后最大化的数组和 LeetCode T134 加油站 LeetCode T135 分发糖果
35 0
|
算法 C++
【每日算法Day 109】五大解法,带你深入了解完全背包方案数
【每日算法Day 109】五大解法,带你深入了解完全背包方案数
106 0
多重背包问题与优化(裸题)(一)
多重背包问题与优化(裸题)
127 0
多重背包问题与优化(裸题)(一)
多重背包问题与优化(裸题)(二)
多重背包问题与优化(裸题)
155 0
多重背包问题与优化(裸题)(二)
[LDUoj 倍增] 题解(下)
F. 异象石 难度适中 G. 暗的连锁 难度适中 H. 点的距离 板子题 l. 晨跑 大水题 J. 货物运输 较简单 K. 数三角形 组合数学简单题
114 0
[LDUoj 倍增] 题解(下)