Submission #198420

# Submission time Handle Problem Language Result Execution time Memory
198420 2020-01-26T02:25:52 Z model_code Traffickers (RMI18_traffickers) C++14
60 / 100
3500 ms 29560 KB
/**
 * Description: Split tree in blocks of size sqrt(N) by introducing
 * supplemental vertices. Guarantees O(sqrt(N)) for all queries.
 *
 * Author: Catalin Francu
 **/
#include <math.h>
#include <stdio.h>

#define MAX_N 30000
#define MAX_D 20
#define NIL -1

typedef unsigned short u16;

typedef struct {
  int node, next;
} cell;

cell c[3 * MAX_N];
int head[2 * MAX_N], tail[2 * MAX_N]; /* adjacency lists */
int d[2 * MAX_N]; /* vertex depths */
int p[2 * MAX_N]; /* parents */
int ba[2 * MAX_N]; /* block ancestors */
int n, supn;
int unused; /* pointer to first unused position in c */

/* Variables for cargo info. cargo[d][mod][u] stores the number of units */
/* at times of the form dk+mod for ALL vertices between u (inclusively) */
/* and the block ancestor of u (exclusively) */
int cbuffer[(MAX_D + 1) * MAX_D * MAX_N];
int *cargo[MAX_D + 1][MAX_D]; /* entry points into cbuffer */

/* precompute the (D + 1 choose 2) offsets of cargo arrays, so that we */
/* don't waste half the space */
void initCargo() {
  int *ptr = cbuffer;
  for (int d = 1; d <= MAX_D; d++) {
    for (int i = 0; i < d; i++) {
      cargo[d][i] = ptr;
      ptr += 2 * n;
    }
  }
}

/* add v to u's adjacency list */
void addEdge(int u, int v) {
  c[unused].node = v;
  c[unused].next = NIL;
  if (head[u] == NIL) {
    head[u] = unused;
  } else {
    c[tail[u]].next = unused;
  }
  tail[u] = unused++;
}

void readTree() {
  int u, v;

  scanf("%d", &n);
  supn = n;

  for (u = 0; u < n; u++) {
    head[u] = tail[u] = NIL;
  }

  for (int i = 0; i < n - 1; i++) {
    scanf("%d%d", &u, &v);
    u--; v--;      /* zero-based vertices */
    addEdge(u, v);
    addEdge(v, u);
  }
}

/* removes the child pointed at by ptr from the adjacency list of u */
void removeChild(int u, int ptr, int prev) {
  if (ptr == head[u]) {
    head[u] = c[ptr].next;
  } else {
    c[prev].next = c[ptr].next;
  }

  if (ptr == tail[u]) {
    tail[u] = prev;
  }
}

/* move u's children, up to and including ptr, to a supplemental vertex */
/* this can actually create blocks of size 2 * blockSize */
void moveChildren(int u, int ptr) {
  addEdge(u, supn);

  /* pointers */
  head[supn] = head[u];
  tail[supn] = ptr;
  head[u] = c[ptr].next;
  c[ptr].next = NIL;

  /* depth */
  d[supn] = 1 + d[u];

  /* parents */
  p[supn] = u;
  for (int i = head[supn]; i != NIL; i = c[i].next) {
    p[c[i].node] = supn;
  }

  supn++;
}

/* to avoid some corner cases, put a supplemental node above the root */
void fakeRoot() {
  head[supn] = tail[supn] = NIL;
  addEdge(supn, 0);
  p[0] = supn;
  p[supn] = NIL;
  d[supn] = 0;
  supn++;
}

/* (1) compute depths and parents */
/* (2) remove parents from neighbor lists to obtain proper child lists */
/* (3) insert vertices to split the tree into blocks of size blockSize */
/* returns the number of vertices not yet assigned to a block */
int depthDfs(int u, int parent, int depth, int blockSize) {
  d[u] = depth;
  p[u] = parent;

  int ptr = head[u], prev = NIL, result = 0;
  while (ptr != NIL && c[ptr].node < n) { /* supplementary vertices are at the end */
    if (c[ptr].node == parent) {
      /* remove parent from neighbor list */
      removeChild(u, ptr, prev);
      ptr = c[ptr].next;     /* prev stays the same */
    } else {
      result += depthDfs(c[ptr].node, u, depth + 2, blockSize);

      if (result >= blockSize) {
        moveChildren(u, ptr);
        prev = NIL;
        ptr = head[u];
        result = 0;
      } else {
        prev = ptr;
        ptr = c[ptr].next;
      }
    }
  }

  return 1 + result; /* include u itself */
}

/* compute the block ancestor of each vertex; returns the number of
   descendants not assigned to a block */
void blockAncestorDfs(int u, int ancestor) {
  ba[u] = ancestor;
  if (u >= n) {
    ancestor = u;
  }

  for (int ptr = head[u]; ptr != NIL; ptr = c[ptr].next) {
    blockAncestorDfs(c[ptr].node, ancestor);
  }
}

int lca(int u, int v) {
  /* block by block */
  while (ba[u] != ba[v]) {
    if (d[ba[u]] > d[ba[v]]) {
      u = ba[u];
    } else {
      v = ba[v];
    }
  }

  /* vertex by vertex */
  while (u != v) {
    if (d[u] > d[v]) {
      u = p[u];
    } else {
      v = p[v];
    }
  }

  /* if the LCA is a supplemental node, move above it */
  return (u >= n) ? p[u] : u;
}

/* add value to cargo[dist][mod] to all the descendants of u in the same */
/* block as u */
void addTraffickerDfs(int u, int dist, int mod, int value) {
  for (int j = mod; j < dist; j++) {
    cargo[dist][j][u] += value;
  }

  if (u < n) { /* cutoff at supplemental vertices */
    for (int ptr = head[u]; ptr != NIL; ptr = c[ptr].next) {
      addTraffickerDfs(c[ptr].node, dist, mod, value);
    }
  }
}

/* Add a trafficker on the [u, v) path, where v is an ancestor of u or NIL. */
/* At vertex u the trafficker delivers at times of the form dist * k + mod. */
/* At each subsequent vertex the times are incremented by step. */
void addTraffickerPath(int u, int v, int dist, int mod, int step, int value) {
  while (u != v) {
    if (u < n) {
      addTraffickerDfs(u, dist, mod, value);
      mod += step;
    }
    u = p[u];
  }
}

void addTrafficker(int u, int v, int value) {
  int l = lca(u, v);
  int dist = (d[u] + d[v] - 2 * d[l]) / 2 + 1;
  addTraffickerPath(u, p[l], dist, 0, +1, value);
  addTraffickerPath(v, l, dist, dist - 1, -1, value);
}

/* how many traffickers deliver goods on the path u -> root inclusively, */
/* betweeen 0 and t inclusively? */
long long countRoot(int u, int t) {
  long long result = 0;

  while (u != NIL) {
    for (int i = 1; i <= MAX_D; i++) {
      long long lo = cargo[i][t % i][u];
      long long all = cargo[i][i - 1][u];
      result += t / i * all + lo;
    }
    u = ba[u];
  }

  return result;
}

/* how many traffickers deliver goods on the path u -> v inclusively, */
/* betweeen 0 and t inclusively? */
long long countOrigin(int u, int v, int lca, int t) {
  long long result =  countRoot(u, t) + countRoot(v, t) - countRoot(lca, t);
  if (lca) { /* not root */
    result -= countRoot(p[lca], t);
  }
  return result;
}

long long countTraffickers(int u, int v, int t1, int t2) {
  int l = lca(u, v);
  long long result = countOrigin(u, v, l, t2);
  if (t1) {
    result -= countOrigin(u, v, l, t1 - 1);
  }
  return result;
}

int main() {
  readTree();
  initCargo();
  int blockSize = sqrt(n) / 2; /* determined experimentally */
  depthDfs(0, NIL, 1, blockSize);
  fakeRoot();
  blockAncestorDfs(supn - 1, NIL);

  int numQueries, type, u, v, t1, t2;

  /* read the initial traffickers */
  scanf("%d", &numQueries);
  while (numQueries--) {
    scanf("%d%d", &u, &v);
    addTrafficker(u - 1, v - 1, 1);
  }

  /* read the remaining queries */
  scanf("%d", &numQueries);
  while (numQueries--) {
    scanf("%d%d%d", &type, &u, &v);
    if (type == 1) {
      addTrafficker(u - 1, v - 1, 1);
    } else if (type == 2) {
      addTrafficker(u - 1, v - 1, -1);
    } else {
      scanf("%d%d", &t1, &t2);
      long long answer = countTraffickers(u - 1, v - 1,  t1, t2);
      printf("%lld\n", answer);
    }
  }

  return 0;
}

Compilation message

traffickers.cpp: In function 'void readTree()':
traffickers.cpp:61:8: warning: ignoring return value of 'int scanf(const char*, ...)', declared with attribute warn_unused_result [-Wunused-result]
   scanf("%d", &n);
   ~~~~~^~~~~~~~~~
traffickers.cpp:69:10: warning: ignoring return value of 'int scanf(const char*, ...)', declared with attribute warn_unused_result [-Wunused-result]
     scanf("%d%d", &u, &v);
     ~~~~~^~~~~~~~~~~~~~~~
traffickers.cpp: In function 'int main()':
traffickers.cpp:271:8: warning: ignoring return value of 'int scanf(const char*, ...)', declared with attribute warn_unused_result [-Wunused-result]
   scanf("%d", &numQueries);
   ~~~~~^~~~~~~~~~~~~~~~~~~
traffickers.cpp:273:10: warning: ignoring return value of 'int scanf(const char*, ...)', declared with attribute warn_unused_result [-Wunused-result]
     scanf("%d%d", &u, &v);
     ~~~~~^~~~~~~~~~~~~~~~
traffickers.cpp:278:8: warning: ignoring return value of 'int scanf(const char*, ...)', declared with attribute warn_unused_result [-Wunused-result]
   scanf("%d", &numQueries);
   ~~~~~^~~~~~~~~~~~~~~~~~~
traffickers.cpp:280:10: warning: ignoring return value of 'int scanf(const char*, ...)', declared with attribute warn_unused_result [-Wunused-result]
     scanf("%d%d%d", &type, &u, &v);
     ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
traffickers.cpp:286:12: warning: ignoring return value of 'int scanf(const char*, ...)', declared with attribute warn_unused_result [-Wunused-result]
       scanf("%d%d", &t1, &t2);
       ~~~~~^~~~~~~~~~~~~~~~~~
# Verdict Execution time Memory Grader output
1 Correct 6 ms 376 KB Output is correct
2 Correct 13 ms 2040 KB Output is correct
3 Correct 13 ms 2040 KB Output is correct
# Verdict Execution time Memory Grader output
1 Correct 365 ms 10232 KB Output is correct
2 Correct 353 ms 9336 KB Output is correct
3 Correct 547 ms 10232 KB Output is correct
4 Correct 539 ms 10488 KB Output is correct
5 Correct 394 ms 10232 KB Output is correct
6 Correct 507 ms 10360 KB Output is correct
7 Correct 412 ms 10360 KB Output is correct
8 Correct 353 ms 10360 KB Output is correct
9 Correct 390 ms 10484 KB Output is correct
# Verdict Execution time Memory Grader output
1 Execution timed out 3582 ms 28536 KB Time limit exceeded
2 Execution timed out 3582 ms 29048 KB Time limit exceeded
3 Execution timed out 3574 ms 28920 KB Time limit exceeded
4 Execution timed out 3574 ms 27768 KB Time limit exceeded
5 Execution timed out 3567 ms 27512 KB Time limit exceeded
6 Execution timed out 3573 ms 29216 KB Time limit exceeded
7 Execution timed out 3572 ms 29560 KB Time limit exceeded
8 Execution timed out 3576 ms 29000 KB Time limit exceeded