eJOI2019 简要题解

A. XORanges

化简一下可得,区间长度为奇数答案才可能不为 0 0 ,此时答案就是 l , l + 2 , , u l, l+2, \dots, u 这些位置数的异或和。奇偶分别维护一个树状数组即可。时间复杂度 Θ ( n + q log n ) \Theta(n + q\log n)

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int N = 100010;

int a[N * 2];
int fw[2][N];

int lowBit(int k) { return k & -k; }

int qr(int* fw, int k) {
  int ret = 0;
  while (k) {
    ret ^= fw[k];
    k &= k - 1;
  }
  return ret;
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  int n, q;
  scanf("%d%d", &n, &q);
  for (int i = 1; i <= n; ++i)
    scanf("%d", &a[i]);
  for (int i = 1; i <= n; ++i)
    fw[i & 1][(i + 1) >> 1] = fw[i & 1][(i - 1) >> 1] ^ a[i];
  n = (n + 1) >> 1;
  for (int j = 0; j < 2; ++j)
    for (int i = n; i; --i)
      fw[j][i] ^= fw[j][i & (i - 1)];
  while (q--) {
    int opt, x, y;
    scanf("%d%d%d", &opt, &x, &y);
    if (opt == 1) {
      int modi = a[x] ^ y;
      int j = x & 1;
      for (int k = (x + 1) >> 1; k <= n; k += lowBit(k))
        fw[j][k] ^= modi;
      a[x] = y;
    } else if ((x ^ y) & 1)
      puts("0");
    else
      printf("%d\n", qr(fw[x & 1], (x - 1) >> 1) ^ qr(fw[x & 1], (y + 1) >> 1));
  }

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

B. Rack

减一之后就是 n n 位的 bit-reverse。时间复杂度 Θ ( n ) \Theta(n) Θ ( log n + log k ) \Theta(\log n + \log k)

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int P = 1000000007;

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  int n;
  ll k;
  scanf("%d%lld", &n, &k);
  --k;
  int ans = 0;
  while (n--) {
    ans = (ans << 1) | (k & 1);
    k >>= 1;
    if (ans >= P) ans -= P;
  }
  if (++ans == P) ans = 0;
  printf("%d\n", ans);

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

C. Covering

如果一个位置周围有至少 3 3 个特殊格子,显然无解。如果一个位置周围有 2 2 个特殊格子,可以看成那两个特殊格子之间连边。如果有两个特殊格子贴边,那么它们的状态被唯一确定,可以唯一确定连通块的每个特殊格子摆法。如果连通块内有 &gt; n &gt;n 条边,无解。如果连通块内有 n n 条边,无论如何方案应该都是与这些特殊格子四相邻的恰好都被覆盖一次。如果连通块内有 n 1 n - 1 条边,从恰相邻一个特殊格子的数中恰有一个可以不被选,减去那个最小的即可。时间复杂度 Θ ( n m ) \Theta(nm)

#include <cstdio>

#include <functional>
#include <vector>
#include <numeric>

#define GG do { puts("No"); return 0; } while (false)

using namespace std;

typedef long long ll;

int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};

int main() {

  int n, m;
  scanf("%d%d", &n, &m);
  
  vector<vector<int>> a(n + 2, vector<int>(m + 2, -2e9));
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      scanf("%d", &a[i][j]);
  int k;
  ll ans = 0;
  scanf("%d", &k);
  vector<vector<int>> label(n + 2, vector<int>(m + 2, 0));
  vector<int> f(k + 1), mn(k + 1, 1e9), cn(k + 1, 1);
  for (int i = 1; i <= k; ++i) {
    int x, y;
    scanf("%d%d", &x, &y);
    ++x; ++y;
    ans += a[x][y];
    label[x][y] = i;
    for (int d = 0; d < 4; ++d)
      ans += a[x + dx[d]][y + dy[d]];
  }
  iota(f.begin(), f.end(), 0);
  function<int(int)> find = [&](int x) { return f[x] == x ? x : f[x] = find(f[x]); };
  function<void(int, int)> merge = [&](int u, int v) {
    if (u != v) {
      f[v] = u;
      mn[u] = min(mn[u], mn[v]);
      cn[u] += cn[v];
    }
  };
  for (int i = 1; i <= n; ++i) {
    if (label[i][1])
      mn[label[i][1]] = -2e9;
    if (label[i][m])
      mn[label[i][m]] = -2e9;
  }
  for (int i = 1; i <= m; ++i) {
    if (label[1][i])
      mn[label[1][i]] = -2e9;
    if (label[n][i])
      mn[label[n][i]] = -2e9;
  }
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j) {
      int cnt = 0;
      for (int d = 0; d < 4; ++d)
        if (label[i + dx[d]][j + dy[d]])
          ++cnt;
      if (cnt >= 3 || label[i][j] && cnt >= 2) GG;
      if (label[i][j] && cnt) {
        for (int d = 0; d < 4; ++d)
          if (label[i + dx[d]][j + dy[d]]) {
            int u = find(label[i][j]), v = find(label[i + dx[d]][j + dy[d]]);
            merge(u, v);
            if (--cn[u] < 0) GG;
          }
        ans -= a[i][j];
      } else if (cnt == 2) {
        int u = 0, v = 0;
        for (int d = 0; d < 4; ++d)
          if (label[i + dx[d]][j + dy[d]]) {
            int cur = find(label[i + dx[d]][j + dy[d]]);
            if (!u) u = cur;
            else v = cur;
          }
        merge(u, v);
        if (--cn[u] < 0) GG;
        ans -= a[i][j];
        mn[u] = min(mn[u], a[i][j]);
      } else if (cnt == 1)
        for (int d = 0; d < 4; ++d)
          if (label[i + dx[d]][j + dy[d]]) {
            int u = find(label[i + dx[d]][j + dy[d]]);
            mn[u] = min(mn[u], a[i][j]);
          }
    }
  for (int i = 1; i <= k; ++i)
    if (f[i] == i && cn[i])
      ans -= mn[i];
  if (ans < 0) GG;
  printf("%lld\n", ans);
  
  return 0;
}

D. Tower

不难猜到解能达到下界 s = log 2 q + 1 s = \lceil \log_2 q \rceil + 1 (当 q = 1 q=1 时为特例)
考虑构造,如果选择区间均为 [ 1 , b a c k ] / [ 2 , b a c k ] [1, back]/[2, back] ,那么数值就是不断 t = 2 t 1 , t = 2 t , , t = 2 t + 1 , t = 2 t , t = 2t-1, t=2t, \dots, t=2t+1, t=2t, \dots ,考虑二进制位差分即可。

#include <cstdio>

#include <functional>
#include <vector>
#include <algorithm>
#include <numeric>

using namespace std;

typedef long long ll;

int main() {
  
  int t;
  scanf("%d", &t);
  while (t--) {
    ll q;
    scanf("%lld", &q);
    if (q == 1) {
      puts("0");
      continue;
    }
    if (!(q & (q - 1))) {
      int s = 0;
      while ((1LL << s) != q) ++s;
      printf("%d\n", s + 1);
      for (int i = 1; i <= s + 1; ++i)
        printf("%d %d\n", 1, i);
      continue;
    }
    int s = 0;
    while ((1LL << s) < q) ++s;
    printf("%d\n", s + 1);
    puts("1 1");
    int cnt = 1;
    int res = 1;
    for (int i = s - 1; i >= 0; --i) {
      if ((q >> i) & 1) {
        if (((q << 1) >> i) & 1)
          ;
        else
          res = 3 - res;
      } else {
        if (((q << 1) >> i) & 1)
          res = 3 - res;
      }
      printf("%d %d\n", res, ++cnt);
    }
  }
  
  return 0;
}

E. Colouring a rectangle

黑白染色后两部分独立,线段树优化 DP。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

struct Node {
  ll v, lazy;
  int l, r;
  Node *ls, *rs;

  ll get() const { return v + lazy; }

  void upd() {
    v = max(ls->get(), rs->get());
  }

  void pd() {
    if (lazy) {
      v += lazy;
      ls->lazy += lazy;
      rs->lazy += lazy;
      lazy = 0;
    }
  }

  void ch(int l, int r, ll v) {
    if (this->l == l && this->r == r) {
      lazy += v;
      return;
    }
    pd();
    if (r <= ls->r)
      ls->ch(l, r, v);
    else if (l >= rs->l)
      rs->ch(l, r, v);
    else {
      ls->ch(l, ls->r, v);
      rs->ch(rs->l, r, v);
    }
    upd();
  }
};

const int N = 200010;

int n, m;
int a[N * 2], b[N * 2];

Node* build(int l, int r) {
  static Node pool[N * 4], *top = pool;
  Node* p = top++;
  p->l = l;
  p->r = r;
  if (l == r)
    return p;
  int mid = (l + r) >> 1;
  p->ls = build(l, mid);
  p->rs = build(mid + 1, r);
  return p;
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n + m - 1; ++i) scanf("%d", &a[i]);
  for (int i = 1; i <= n + m - 1; ++i) scanf("%d", &b[i]);
  if (n < m) {
    swap(n, m);
  } else
    reverse(a + 1, a + n + m);
  Node *seg1 = build(0, n + 1), *seg2 = build(0, n + 1);
  for (int i = 1; i <= n + m - 1; ++i) {
    if (i & 1) seg1->ch(i / 2 + 1, i / 2 + 1, seg1->get() + b[i]);
    else seg2->ch(i / 2, i / 2, seg2->get() + b[i]);
    if (i >= n) {
      int len = i - n + 1;
      if (i & 1)
        seg1->ch(0, max(0, i / 2 + 1 - len), a[i - n + 1]);
      else
        seg2->ch(0, max(0, i / 2 - len), a[i - n + 1]);
    }
    if (i == n + m - 1) break;
    if (i >= m) {
      int len = m;
      if (i <= m * 2 - 1)
        len = i - m + 1;
      if (i & 1)
        seg1->ch(0, i / 2 + 1 - len, a[n + m * 2 - i - 1]);
      else
        seg2->ch(0, i / 2 - len, a[n + m * 2 - i - 1]);
    }
  }
  printf("%lld\n", accumulate(a + 1, a + n + m, 0LL) + accumulate(b + 1, b + n + m, 0LL) - seg1->get() - seg2->get());

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

F. Adventure

最短路。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <numeric>
#include <functional>
#include <queue>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;

const int N = 510;

int n, m;
char s[N][N];
int dis[N][N];
queue<pair<int, int>> pq[4];

int dx[] = {-1, 0, 1, 0},
    dy[] = {0, 1, 0, -1};

int dir[256];

int main() {
//	freopen("test.in", "r", stdin);
//	freopen("test.out", "w", stdout);
	
	dir['N'] = 0;
	dir['E'] = 1;
	dir['S'] = 2;
	dir['W'] = 3;
	
	scanf("%d%d", &n, &m);
	if (n == 1 && m == 1) {
		puts("0");
		return 0;
	}
	for (int i = 1; i <= n; ++i)
		scanf("%s", s[i] + 1);
	memset(dis, -1, sizeof(dis));
	pq[0].emplace(1, 1);
	dis[1][1] = 0;
	int r = -1;
	for (int i = 0; i % 4 != r; ++i) {
		if (pq[i % 4].empty()) continue;
		else {
			r = i % 4;
			while (!pq[r].empty()) {
				int x, y;
				tie(x, y) = pq[r].front();
				if (x == n && y == m) {
					printf("%d\n", dis[x][y]);
					return 0;
				}
				pq[r].pop();
				if (dis[x][y] % 4 != r || s[x][y] == 'X') continue;
				for (int i = 0; i < 4; ++i) {
					int gx = x + dx[(dir[s[x][y]] + i) % 4],
					    gy = y + dy[(dir[s[x][y]] + i) % 4];
					if (gx < 1 || gx > n || gy < 1 || gy > m) continue;
					if (dis[gx][gy] == -1 || dis[gx][gy] > dis[x][y] + i) {
						dis[gx][gy] = dis[x][y] + i;
						pq[dis[gx][gy] % 4].emplace(gx, gy);
					}
				}
			}
		}
	}
	puts("-1");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/EI_Captain/article/details/100547706