JOISC 2020 部分题目简要题解

从这里开始

  感觉每天打比赛都在丢人。感觉离滚蛋不远的次数 +1

Day 1

Problem A ビルの飾りつけ 4

  猜想可行的数量是某个区间。然后 dp 即可。

Code

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

template <typename T>
boolean vmin(T& a, T b) {
	return (a > b) ? (a = b, true) : (false);
}
template <typename T>
boolean vmax(T& a, T b) {
	return (a < b) ? (a = b, true) : (false);
}

template <typename T>
T smax(T x) {
	return x;
}
template <typename T>
T smin(T x) {
	return x;
}

#define ll long long
#define ull unsigned long long

const int inf = (signed) (~0u >> 2);
const ll llf = (signed ll) (~0ull >> 2);

typedef class Input {
	protected:
		const static int limit = 65536;
		FILE* file; 

		int ss, st;
		char buf[limit];
	public:
		
		Input() : file(NULL)	{	};
		Input(FILE* file) : file(file) {	}

		void open(FILE *file) {
			this->file = file;
		}

		void open(const char* filename) {
			file = fopen(filename, "r");
		}

		char pick() {
			if (ss == st)
				st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl;
			return buf[ss++];
		}
} Input;

#define digit(_x) ((_x) >= '0' && (_x) <= '9')

Input& operator >> (Input& in, unsigned& u) {
	char x;
	while (~(x = in.pick()) && !digit(x));
	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
	return in;
}

Input& operator >> (Input& in, unsigned long long& u) {
	char x;
	while (~(x = in.pick()) && !digit(x));
	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
	return in;
}

Input& operator >> (Input& in, int& u) {
	char x;
	while (~(x = in.pick()) && !digit(x) && x != '-');
	int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
	u *= aflag;
	return in;
}

Input& operator >> (Input& in, long long& u) {
	char x;
	while (~(x = in.pick()) && !digit(x) && x != '-');
	int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
	u *= aflag;
	return in;
}

Input& operator >> (Input& in, double& u) {
	char x;
	while (~(x = in.pick()) && !digit(x) && x != '-');
	int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
	if (x == '.') {
		double dec = 1;
		for ( ; ~(x = in.pick()) && digit(x); u = u + (dec *= 0.1) * (x - '0'));
	}
	u *= aflag;
	return in;
}

Input& operator >> (Input& in, char* str) {
	char x;
	while (~(x = in.pick()) && x != '
' && x != ' ')
		*(str++) = x;
	*str = 0;
	return in;
}

Input in (stdin);

typedef class Output {
	protected:
		const static int Limit = 65536;
		char *tp, *ed;
		char buf[Limit];
		FILE* file;
		int precision;

		void flush() {
			fwrite(buf, 1, tp - buf, file);
			fflush(file);
			tp = buf;
		}

	public:

		Output() {	}
		Output(FILE* file) : tp(buf), ed(buf + Limit), file(file), precision(6) {	}
		Output(const char *str) : tp(buf), ed(buf + Limit), precision(6) {
			file = fopen(str, "w");
		}
		~Output() {
			flush();
		}

		void put(char x) {
			if (tp == ed)
				flush();
			*(tp++) = x;
		}

		int get_precision() {
			return precision;
		}
		void set_percision(int x) {
			precision = x;
		}
} Output;

Output& operator << (Output& out, int x) {
	static char buf[35];
	static char * const lim = buf + 34;
	if (!x)
		out.put('0');
	else {
		if (x < 0)
			out.put('-'), x = -x;
		char *tp = lim;
		for ( ; x; *(--tp) = x % 10, x /= 10);
		for ( ; tp != lim; out.put(*(tp++) + '0'));
	}
	return out;
}

Output& operator << (Output& out, long long x) {
	static char buf[36];
	static char * const lim = buf + 34;
	if (!x)
		out.put('0');
	else {
		if (x < 0)
			out.put('-'), x = -x;
		char *tp = lim;
		for ( ; x; *(--tp) = x % 10, x /= 10);
		for ( ; tp != lim; out.put(*(tp++) + '0'));
	}
	return out;
}

Output& operator << (Output& out, unsigned x) {
	static char buf[35];
	static char * const lim = buf + 34;
	if (!x)
		out.put('0');
	else {
		char *tp = lim;
		for ( ; x; *(--tp) = x % 10, x /= 10);
		for ( ; tp != lim; out.put(*(tp++) + '0'));
	}
	return out;
}

Output& operator << (Output& out, char x)  {
	out.put(x);
	return out;
}

Output& operator << (Output& out, const char* str) {
	for ( ; *str; out.put(*(str++)));
	return out;
}

Output& operator << (Output& out, double x) {
	int y = x;
	x -= y;
	out << y << '.';
	for (int i = out.get_precision(); i; i--, y = x * 10, x = x * 10 - y, out.put(y + '0'));
	return out;
}

Output out (stdout);

const int N = 1e6 + 3;

int n;
int a[N][2];
int fr[N][2][2]; // 0: mi, 1 : mx

int main() {
	in >> n;
	n <<= 1;
	int hn = n >> 1;
	for (int i = 0; i < 2; i++) {
		for (int j = 1; j <= n; j++) {
			in >> a[j][i];
		}
	}
#define fl fr
	fl[n][0][0] = fl[n][0][1] = 1;
	fl[n][1][0] = fl[n][1][1] = 0;
	for (int i = n; --i; ) {
		int (*f)[2] = fl[i + 1], (*g)[2] = fl[i];
		g[0][0] = g[1][0] = inf;
		g[0][1] = g[1][1] = -inf;
		for (int s = 0; s < 2; s++) {
			for (int t = 0; t < 2; t++) {
				if (a[i + 1][s] >= a[i][t]) {
					vmin(g[t][0], f[s][0] + !t);
					vmax(g[t][1], f[s][1] + !t);
				}
			}
		}
	}
	if (!(hn >= fl[1][0][0] && hn <= fl[1][0][1]) && !(hn >= fl[1][1][0] && hn <= fl[1][1][1])) {
		out << "-1" << '
';
		return 0;
	}
	int cntA = 0, ls = 0;
	for (int i = 1; i < n; i++) {
		int need = hn - cntA - 1;
		if (a[i][0] >= ls && ((a[i + 1][0] >= a[i][0] && need >= fl[i + 1][0][0] && need <= fl[i + 1][0][1]) || (a[i + 1][1] >= a[i][0] && need >= fl[i + 1][1][0] && need <= fl[i + 1][1][1]))) {
			ls = a[i][0];
			cntA++;
			out << 'A';	
		} else {
			ls = a[i][1];
			out << 'B';
		}
	}
	out << "BA"[cntA < hn] << '
';
	return 0;
}

Problem B 美味しい美味しいハンバーグ

  考虑 $lmx, rmi, dmx, umi$ 这四个值,如果放置一根竹签后其中一个改变了,比如是 $lmx$,那么可以通过平移使得 $x = lmx$。

  当 $K leqslant 3$ 的时候根据鸽巢原理,一定有一根插在角落,爆搜就行了。

  当 $K = 4$ 并且爆搜找不到解,说明每个边界上都有一根竹签。

  因此我们在某个边界找使得它改变量最大的方案。你在想 peach。

  如果一个矩形与至少 3 个边界有交,任意一个满足条件的方案都会存在一根竹签在它内部。满足条件竹签在 $lmx, rmi, dmx, umi$ 围成的矩形内部。因此不用管。

  现在只剩下一个矩形与至多 2 个边界有交的情况,这种情况可以转成某个区间 $[l_1, r_1]$ 和 $[l_2, r_2]$ 中至少有一个点。

  考虑 2-sat,然后对于每个边界分别建一个排点,表示这个边界上的竹签的坐标小于等于 $x_i$ 是否为真。要求在某个区间内就拆成 $x < l$ 为假,$x leqslant r$ 为真。

Code

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

template <typename T>
bool vmax(T& a, T b) {
  return (a < b) ? (a = b, true) : false;
}
template <typename T>
bool vmin(T& a, T b) {
  return (a > b) ? (a = b, true) : false;
}

const int inf = (signed) (~0u >> 1);

typedef class Rectangle {
  public:
    int l, d, r, u;

    Rectangle() { }
    Rectangle(int l, int r, int d, int u) : l(l), d(d), r(r), u(u) {  }

    void read() {
      scanf("%d%d%d%d", &l, &d, &r, &u);
    }
    bool contain(int x, int y) {
      return l <= x && x <= r && d <= y && y <= u;
    }
    Rectangle operator & (Rectangle b) {
      Rectangle rt;
      rt.l = max(l, b.l);
      rt.d = max(d, b.d);
      rt.r = min(r, b.r);
      rt.u = min(u, b.u);
      return rt;
    }
    bool empty() {
      return l > r || d > u;
    }
} Rectangle;

const int N = 2e5 + 5;

int n, K;
Rectangle a[N];

vector<int> ansx, ansy;
void ok() {
  while ((signed) ansx.size() < K) {
    ansx.push_back(ansx.back());
    ansy.push_back(ansy.back());
  }
  for (int i = 0; i < K; i++) {
    printf("%d %d
", ansx[i], ansy[i]);
  }
  exit(0);
}

namespace subtask1 {

  bool cov[N];

  vector<int> foo[5];
  void dfs(int rest) {
    Rectangle ins (-inf, inf, -inf, inf);
    for (int i = 1; i <= n; i++) {
      if (!cov[i]) {
        ins = ins & a[i];
      }
    }
    if (ins.l == -inf) {
      ok();
    }
    if (rest == 1) {
      if (!ins.empty()) {
        ansx.push_back(ins.l);
        ansy.push_back(ins.d);
        ok();
      }
      return;
    }
    vector<int> &idx = foo[rest];
    auto work = [&] (int x, int y) {
      for (int i = 1; i <= n; i++) {
        if (!cov[i] && a[i].contain(x, y)) {
          idx.push_back(i);
          cov[i] = true;
        }
      }
      ansx.push_back(x);
      ansy.push_back(y);
      dfs(rest - 1);
      ansx.pop_back();
      ansy.pop_back();
      for (auto x : idx) {
        cov[x] = false;
      }
      idx.clear();
    };
    work(ins.l, ins.d);
    work(ins.l, ins.u);
    work(ins.r, ins.d);
    work(ins.r, ins.u);
  }

}

namespace subtask2 {

  int n2;

#define _R(x) ((x) << 1)
#define _V(x) (((x) << 1) | 1)
  int R(int x, int y) { return _R(x * n2 + y);  }
  int V(int x, int y) { return _V(x * n2 + y);  }

  vector<bool> vis, ins;
  vector<int> dfn, low, id;
  vector<vector<int>> G;

  void tarjan(int p) {
    static stack<int> S;
    static int dfc = 0, cscc = 0;
    S.push(p);
    vis[p] = ins[p] = true;
    dfn[p] = low[p] = ++dfc;
    for (auto e : G[p]) {
      if (!vis[e]) {
        tarjan(e);
        low[p] = min(low[p], low[e]);
      } else if (ins[e]) {
        low[p] = min(low[p], dfn[e]);
      }
    }
    if (dfn[p] == low[p]) {
      int q;
      ++cscc;
      do {
        q = S.top();
        S.pop();
        id[q] = cscc;
        ins[q] = false;
      } while (q ^ p);
    }
  }

  template <typename T>
    vector<T> discrete(T* arr, int n, int* b) {
      vector<T> ret (arr, arr + n);
      sort(ret.begin(), ret.end());
      ret.erase(unique(ret.begin(), ret.end()), ret.end());
      for (int i = 0; i < n; i++) {
        b[i] = lower_bound(ret.begin(), ret.end(), arr[i]) - ret.begin(); 
      }
      return ret;
    }

  void add_edge(int x, int y) {
    G[x].push_back(y);
    G[y ^ 1].push_back(x ^ 1);
  }

  void solve() {
    n2 = (n + 1) << 1 | 1;
    int n8 = n2 << 3;
    vis.assign(n8, false);
    ins.assign(n8, false);
    dfn.assign(n8, 0);
    low.assign(n8, 0);
    id.assign(n8, -1);
    G.resize(n8, vector<int>());
    vector<int> vx {0}, vy {0};
#define ins ufo
    Rectangle ins (-inf, inf, -inf, inf);
    for (int i = 1; i <= n; i++) {
      vx.push_back(a[i].l - 1);
      vx.push_back(a[i].r);
      vy.push_back(a[i].d - 1);
      vy.push_back(a[i].u);
      ins = ins & a[i];
    }

    for (int i = 0; i < 4; i++) {
      for (int j = 1; j < n2; j++) {
        add_edge(R(i, j - 1), R(i, j));
      }
    }

    auto link = [&] (int s1, int l1, int r1, int s2, int l2, int r2) {
      add_edge(R(s1, l1), V(s2, l2));
      add_edge(R(s1, l1), R(s2, r2));
      add_edge(V(s1, r1), V(s2, l2));
      add_edge(V(s1, r1), R(s2, r2));
    };

    // lrdu
    vector<int> svx = discrete(vx.data(), vx.size(), vx.data());
    vector<int> svy = discrete(vy.data(), vy.size(), vy.data());
    vector<int> _id, ls, rs;
#define between(l, r, x) ((x) >= (l) && (x) <= (r))
    for (int i = 1; i <= n; i++) {
      int si = (i << 1) - 1;
      if (between(a[i].l, a[i].r, ins.l)) {
        _id.push_back(0);
        ls.push_back(vy[si]);
        rs.push_back(vy[si + 1]);
      }
      if (between(a[i].l, a[i].r, ins.r)) {
        _id.push_back(1);
        ls.push_back(vy[si]);
        rs.push_back(vy[si + 1]);
      }
      if (between(a[i].d, a[i].u, ins.d)) {
        _id.push_back(2);
        ls.push_back(vx[si]);
        rs.push_back(vx[si + 1]);
      }
      if (between(a[i].d, a[i].u, ins.u)) {
        _id.push_back(3);
        ls.push_back(vx[si]);
        rs.push_back(vx[si + 1]);
      }
      if (_id.size() == 1u) {
        link(_id[0], ls[0], rs[0], _id[0], ls[0], rs[0]);
      } else if (_id.size() == 2u) {
        link(_id[0], ls[0], rs[0], _id[1], ls[1], rs[1]);
      }
      _id.clear();
      ls.clear();
      rs.clear();
    }

    for (int i = 0; i < n8; i++) {
      if (!vis[i]) {
        tarjan(i);
      }
    }
    ansx.resize(4, 0);
    ansy.resize(4, 0);
    ansx[0] = ins.l;
    ansx[1] = ins.r;
    ansy[2] = ins.d;
    ansy[3] = ins.u;
    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < n2; j++) {
        assert(id[R(i, j)] != id[V(i, j)]);
      }
      for (int j = 0; j < n2; j++) {
        assert(id[R(i, j)] != id[V(i, j)]);
        if (id[R(i, j)] < id[V(i, j)]) {
          if (i < 2) {
            ansy[i] = svy[j];
          } else {
            ansx[i] = svx[j];
          }
          break;
        }
      }
    }
    ok();
  }

}

int main() {
  scanf("%d%d", &n, &K);
  for (int i = 1; i <= n; i++) {
    a[i].read();
  }
  subtask1::dfs(K);
  assert(K == 4);
  subtask2::solve();
  assert(false);
  return 0;
}

Problem C 掃除

  先考虑一下 subtask 3,不难发现它每次相当于是一对前缀或者后缀操作。

  再考虑一下 subtask 4,不难证明被推过至少一次的点一定满足 subtask 3 的性质。

  但是后面加入的点会破坏这个性质。套一个线段树分治就行了。

  又成功成为 loj 最长榜榜首[心情简单.jpg]

  以后再也不在 LCT 外的地方写 splay 了。

Code

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

class Timer {
  public:
    void operator () (const char* s = "") {
      cerr << s << " used: " << clock() << "ms
";
    }
} Timer;

typedef class Input {
  protected:
    const static int limit = 65536;
    FILE* file; 

    int ss, st;
    char buf[limit];
  public:

    Input() : file(NULL)	{	};
    Input(FILE* file) : file(file) {	}

    void open(FILE *file) {
      this->file = file;
    }

    void open(const char* filename) {
      file = fopen(filename, "r");
    }

    char pick() {
      if (ss == st)
        st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl;
      return buf[ss++];
    }
} Input;

#define digit(_x) ((_x) >= '0' && (_x) <= '9')

Input& operator >> (Input& in, unsigned& u) {
  char x;
  while (~(x = in.pick()) && !digit(x));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  return in;
}

Input& operator >> (Input& in, unsigned long long& u) {
  char x;
  while (~(x = in.pick()) && !digit(x));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  return in;
}

Input& operator >> (Input& in, int& u) {
  char x;
  while (~(x = in.pick()) && !digit(x) && x != '-');
  int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  u *= aflag;
  return in;
}

Input& operator >> (Input& in, long long& u) {
  char x;
  while (~(x = in.pick()) && !digit(x) && x != '-');
  int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  u *= aflag;
  return in;
}

Input& operator >> (Input& in, double& u) {
  char x;
  while (~(x = in.pick()) && !digit(x) && x != '-');
  int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  if (x == '.') {
    double dec = 1;
    for ( ; ~(x = in.pick()) && digit(x); u = u + (dec *= 0.1) * (x - '0'));
  }
  u *= aflag;
  return in;
}

Input& operator >> (Input& in, char* str) {
  char x;
  while (~(x = in.pick()) && x != '
' && x != ' ')
    *(str++) = x;
  *str = 0;
  return in;
}

Input in (stdin);

typedef class Output {
  protected:
    const static int Limit = 65536;
    char *tp, *ed;
    char buf[Limit];
    FILE* file;
    int precision;

    void flush() {
      fwrite(buf, 1, tp - buf, file);
      fflush(file);
      tp = buf;
    }

  public:

    Output() {	}
    Output(FILE* file) : tp(buf), ed(buf + Limit), file(file), precision(6) {	}
    Output(const char *str) : tp(buf), ed(buf + Limit), precision(6) {
      file = fopen(str, "w");
    }
    ~Output() {
      flush();
    }

    void put(char x) {
      if (tp == ed)
        flush();
      *(tp++) = x;
    }

    int get_precision() {
      return precision;
    }
    void set_percision(int x) {
      precision = x;
    }
} Output;

Output& operator << (Output& out, int x) {
  static char buf[35];
  static char * const lim = buf + 34;
  if (!x)
    out.put('0');
  else {
    if (x < 0)
      out.put('-'), x = -x;
    char *tp = lim;
    for ( ; x; *(--tp) = x % 10, x /= 10);
    for ( ; tp != lim; out.put(*(tp++) + '0'));
  }
  return out;
}

Output& operator << (Output& out, long long x) {
  static char buf[36];
  static char * const lim = buf + 34;
  if (!x)
    out.put('0');
  else {
    if (x < 0)
      out.put('-'), x = -x;
    char *tp = lim;
    for ( ; x; *(--tp) = x % 10, x /= 10);
    for ( ; tp != lim; out.put(*(tp++) + '0'));
  }
  return out;
}

Output& operator << (Output& out, unsigned x) {
  static char buf[35];
  static char * const lim = buf + 34;
  if (!x)
    out.put('0');
  else {
    char *tp = lim;
    for ( ; x; *(--tp) = x % 10, x /= 10);
    for ( ; tp != lim; out.put(*(tp++) + '0'));
  }
  return out;
}

Output& operator << (Output& out, char x)  {
  out.put(x);
  return out;
}

Output& operator << (Output& out, const char* str) {
  for ( ; *str; out.put(*(str++)));
  return out;
}

Output& operator << (Output& out, double x) {
  int y = x;
  x -= y;
  out << y << '.';
  for (int i = out.get_precision(); i; i--, y = x * 10, x = x * 10 - y, out.put(y + '0'));
  return out;
}

Output out (stdout);

const int N = 1500003;
const int inf = (signed) (~0u >> 2);

template <typename T>
bool vmax(T& a, T b) {
  return (a < b) ? (a = b, true) : false;
}
template <typename T>
bool vmin(T& a, T b) {
  return (a > b) ? (a = b, true) : false;
}

typedef class Node {
  public:
    int x, y;
    int ymi;
    int tgx, tgy;
    Node* fa;
    Node* ch[2];

    Node() {  }
    Node(int x, int y) : x(x), y(y), ymi(y), tgx(-1), tgy(-1), fa(NULL) {
      ch[0] = ch[1] = NULL;
    }

    void upd(int vx, int vy) {
      vmax(x, vx);
      vmax(y, vy);
      vmax(tgx, vx);
      vmax(tgy, vy);
    }
    void push_down() {
      if (~tgx || ~tgy) {
        if (ch[0]) ch[0]->upd(tgx, tgy);
        if (ch[1]) ch[1]->upd(tgx, tgy);
        tgx = tgy = -1;
      }
    }
    void push_up() {
      ymi = y;
      if (ch[0]) vmin(ymi, ch[0]->ymi);
      if (ch[1]) vmin(ymi, ch[1]->ymi);
    }

    void reset() {
      x = y = -1;
      tgx = tgy = -1;
      ymi = -1;
      fa = ch[0] = ch[1] = NULL;
    }

    int which() {
      return !fa ? -1 : fa->ch[1] == this;
    }

    void refresh() {
      if (fa) {
        fa->refresh();
      }
      push_down();
    }

    void qwq() {
      push_down();
      if (ch[0]) {
//        assert(ch[0]->y >= y);
        ch[0]->qwq();
      }
      if (ch[1]) {
//        assert(ch[1]->y <= y);
        ch[1]->qwq();
      }
    }
    void log() {
      push_down();
      if (ch[0]) ch[0]->log();
      cerr << "(" << x << ", " << y << ") ";
      if (ch[1]) ch[1]->log();
    }
} Node;

Node pool[N];

typedef class Splay {
  public:
    Node _L, _R;
    Node* rt;

    void init() {
      rt = NULL;
      _L = Node(-1, inf);
      insert(&_L);
      _R = Node(inf, -1);
      insert(&_R);
    }

    void fix(Node* p, int d) {
      if (p && p->ch[d]) {
        p->ch[d]->fa = p;
      }
    }
    void rotate(Node* p) {
      int d = p->which();
      Node* fa = p->fa;
      int fd = fa->which();
      p->fa = fa->fa;
      if (~fd) p->fa->ch[fd] = p;
      fa->ch[d] = p->ch[d ^ 1];
      fix(fa, d);
      fa->fa = p;
      p->ch[d ^ 1] = fa;
      fa->push_up();
      p->push_up();
    }

    void splay(Node* p, Node* goal = NULL) {
      p->refresh();
      for ( ; p->fa != goal; rotate(p)) {
        if (p->fa->fa != goal) {
          rotate((p->which() == p->fa->which()) ? p->fa : p);
        }
      }
      if (!goal) {
        rt = p;
      }
    }

    void _insert(Node*& p, Node* q) {
      if (!p) {
        p = q;
        return;
      }
      p->push_down();
      Node* &foo = p->ch[(q->x ^ p->x) ? (q->x > p->x) : (q->y < p->y)];
      _insert(foo, q);
      foo->fa = p;
      p->push_up();
    }
    void insert(Node* q) {
      _insert(rt, q);
      splay(q);
    }

    void find_x(int x) {
      Node* p = rt, *q = NULL;
      while (p) {
        p->push_down();
        if (p->x > x) {
          q = p;
          p = p->ch[0];
        } else {
          p = p->ch[1];
        }
      }
      assert(q);
      splay(q);
    }
    void find_y(int y) {
      Node* p = rt, *q = NULL;
      while (p) {
        p->push_down();
        if (p->y > y) {
          q = p;
          p = p->ch[1];
        } else {
          p = p->ch[0];
        }
      }
      assert(q);
      splay(q);
    }

    void remove(Node* p) {
      splay(p);
      //     cerr << "Rem: " << _L.x << " " << _L.y << " " << p->x << " " << p->y << " " << rt->fa << '
';
      Node* q = p->ch[0];
      assert(q);
      while (q->ch[1])
        q = q->ch[1];
      splay(q, p);
      q->ch[1] = p->ch[1];
      q->ch[1]->fa = q;
      q->push_up();
      q->fa = NULL;
      rt = q;
    }
    
    void _find_rect(Node* p, int y, vector<Node*>& v) {
      if (p->y <= y) {
        v.push_back(p);
      }
      for (auto l : p->ch) {
        if (l && l->ymi <= y) {
          _find_rect(l, y, v);
        }
      }
    }
    vector<Node*>& find_rect(int x, int y) {
      static vector<Node*> vec;
      find_x(x);
      vec.clear();
      Node* p = rt->ch[0];
      if (!p || (p && p->ymi > y)) {
        return vec;
      }
      _find_rect(p, y, vec);
      return vec;
    }

    void log() {
      rt->log();
      cerr << '
';
    }
} Splay;

int L, n, q;
int px[N], py[N];
int __cnt = 0;

typedef class Layer {
  public:
    Splay pr;
    Splay sp;
    int sl, sr;

    void init(int l, int r) {
      pr.init();
      sp.init();
      sl = l, sr = r;
      for (int i = l; i <= r; i++) {
        pool[i] = Node(px[i], py[i]);
        pr.insert(pool + i);
      }
    }

    void sweepU(int l) {
      int y = L - l;
      sp.find_x(l);
      sp.rt->ch[0]->upd(-1, y);
      auto& v = pr.find_rect(l, y);
      for (auto p : v) {  
        int x = p->x;
        pr.remove(p);
        *p = Node(x, y);
        sp.insert(p);
      }
    }
    void sweepR(int l) {
      int x = L - l;
      sp.find_y(l);
      sp.rt->ch[1]->upd(x, -1);
      auto& v = pr.find_rect(x, l);
      for (auto p : v) {  
        int y = p->y;
        pr.remove(p);
        *p = Node(x, y);
        sp.insert(p);
      }
    }

    void destory() {
      sp.rt->qwq();
      Node* p = pool + sl;
      for (int i = sl; i <= sr; i++, p++) {
        px[i] = p->x;
        py[i] = p->y;
      }
    }

    void splay(Node* x) {
      Node* rx = x;
      while (rx->fa) rx = rx->fa;
      if (rx == sp.rt) {
        sp.splay(x);
      } else {
        assert(rx == pr.rt);
        pr.splay(x);
      }
    }

    int length() {
      return sr - sl + 1;
    }
} Layer;

int tp;
Layer lf[22];

int main() {
  in >> L >> n >> q;
  for (int i = 1; i <= n; i++) {
    in >> px[i] >> py[i];
  }
//  Timer("reading ends");
  tp = 0;
  lf[0].init(1, n);
//  Timer("initing ends");
  int op, x;
  int _cnt = 0;
//  clock_t last_gap = clock();
  while (q--) {
    in >> op >> x;
 //   auto beg = clock();
    if (op == 1) {
      pool[x].refresh();
      for (int i = 0; i <= tp; i++) {
        if (x <= lf[i].sr) {
          lf[i].splay(pool + x);
          break;
        }
      }
 //     _cnt += 1;
      out << pool[x].x << " " << pool[x].y << '
';
    } else if (op == 2) {
      for (int i = 0; i <= tp; i++) {
        lf[i].sweepR(x);
      }
//      _cnt += __cnt;
    } else if (op == 3) {
      for (int i = 0; i <= tp; i++) {
        lf[i].sweepU(x);
      }
//      _cnt += __cnt;
    } else {
      px[++n] = x;
      in >> py[n];
      int L = n;
      while (~tp && n - L + 1 >= lf[tp].length()) {
        lf[tp].destory();
        L = lf[tp].sl;
        tp--;
      }
      _cnt += n - L;
      lf[++tp].init(L, n);
    }
//    auto end = clock();
//    if (end - beg > 200) {
//      cerr << op << " " << x << " " << __cnt << '
';
//    }
//    if (!(q % 1000)) {
//      cerr << _cnt << '
';
//      Timer(), _cnt = 0;
//      last_gap = clock();
//    }
//    if (clock() - last_gap > 1000) {
//      cerr << end - beg << " " << op << " " << x << '
';
//    }
  }
  return 0;
}

Day 2

  总码量最小的一天。[心情简单.jpg]

  不知道为什么我看完题会有 t2 是最难的题的错觉。可能是看完题就觉得细节会很多。

Problem A カメレオンの恋

  考虑两个点之间有边当且仅当它们颜色相同或者其中一个喜欢另一个。显然每个点的度数要么为 1 要么为 3。

  如果度数为 1 可以直接确定。否择假设和 $x$ 相连另外三个点是 $a, b, c$,询问任意两个加上 $x$ 可以辨认出 $L_x$,因为包含它所有询问集合的结果为 2。

  如果已知 $X$ 那么大力二分就行了。

  如果不知道就拆成若干个极大独立集。因为每个点的度数都不超过 3,所以可以拆成不超过 4 个独立集。然后大力二分就行了。

Code

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

typedef vector<int> vec;

vec append(vec a, int x) {
  a.push_back(x);
  return a;
} 
vec subarr(const vec& a, int l, int r) {
  vec rt;
  for (int i = l; i <= r; i++) {
    rt.push_back(a[i]);
  }
  return rt;
}

vector<vec> G;

void add_edge(int u, int v) {
  G[u].push_back(v);
  G[v].push_back(u);
}

void find_edge(const vec& D, int x) {
  int L = 0;
  auto has_edge = [&] (const vec& a, int x) {
    return !a.empty() && Query(append(a, x)) <= (signed) a.size();
  };
  while (has_edge(subarr(D, L, (signed) D.size() - 1), x)) {
    int l = L, r = (signed) D.size() - 1, mid;
    while (l <= r) {
      mid = (l + r) >> 1;
      if (has_edge(subarr(D, l, mid), x)) {
        r = mid - 1;
      } else {
        l = mid + 1;
      }
    }
    add_edge(D[++r], x);
    L = ++r;
  }  
}

vec work(vec P, vector<vec>& Ds) {
  vec D, rt;
  auto has_edge = [&] (const vec& a, int x) {
    return !a.empty() && Query(append(a, x)) <= (signed) a.size();
  };
  for (auto x : P) {
    if (!has_edge(D, x)) {
      D.push_back(x);
    } else {
      rt.push_back(x);
    }
  }
  Ds.push_back(D);
  return rt;
}

void Solve(int N) {
  int N2 = N << 1;
  vector<int> L (N2 + 1, 0);
  vector<bool> vis (N2 + 1, false);
  G.resize(N2 + 1);
  vec P;
  P.reserve(N2);
  for (int i = 1; i <= N2; i++) {
    P.push_back(i);
  }
  vector<vec> D;
  while (!P.empty()) {
    P = work(P, D);
  }
  for (int i = 0; i < (signed) D.size(); i++) {
    for (int j = i + 1; j < (signed) D.size(); j++) {
      vec& a = D[i], &b = D[j];
      for (auto p : a) {
        find_edge(b, p);
      }
    }
  }
  for (int i = 1; i <= N2; i++) {
    if (!vis[i]) {
      vec& nearby = G[i];
      if (nearby.size() == 3u) {
        random_shuffle(nearby.begin(), nearby.end());
        for (int u = 0; u < 3; u++) {
          for (int v = u + 1; v < 3; v++) {
            if (L[nearby[3 ^ u ^ v]] != i && Query(vec{i, nearby[u], nearby[v]}) == 1) {
              L[i] = nearby[3 ^ u ^ v];
              u = 3;
              break;
            }
          }
        }
      } else {
        assert(nearby.size() == 1u);
        Answer(i, nearby[0]);
        vis[i] = vis[nearby[0]] = true;
      }
    }
  }
  for (int i = 1; i <= N2; i++) {
    if (vis[i]) {
      continue;
    }
    for (auto x : G[i]) {
      if (L[i] != x && L[x] != i && !vis[x]) {
        Answer(i, x);
        vis[i] = vis[x] = true;
      }
    }
  }
}

Problem B ジョイッターで友だちをつくろう

  不难发现题目在做这样一件事:

  • 如果 $x$ 向 $y$ 并且 $y$ 向 $x$ 连边,然后把它们缩成一个点
  • 如果 $x$ 中某个点有向 $y$ 连边,那么答案加上 $sz_y$

  大力维护一下一个点到哪些集合,一个集合有哪些出边和入边。

  启发式合并即可。注意合并后可能会产生新的合并。

Code

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

const int N = 2e5 + 5;

#define ll long long

int n, m;
int uf[N], sz[N];
set<int> S[N];
set<int> Gi[N], Go[N];
multiset<int> Gci[N], Gco[N];

int find(int x) {
  return (uf[x] == x) ? (x) : (uf[x] = find(uf[x]));
}

void add_edge(int u, int v) {
  v = find(v);
  Go[u].insert(v);
  Gi[v].insert(u);
  u = find(u);
  Gco[u].insert(v);
  Gci[v].insert(u);
} 
void remove_edge(int u, int v) {
  v = find(v);
  Go[u].erase(v);
  Gi[v].erase(u);
  u = find(u);
  Gco[u].erase(v);
  Gci[v].erase(u);
}

ll C2(int n) {
  return (1ll * n * (n - 1));
}

ll ans = 0;
void work(int x, int y) {
  static vector<pair<int, int>> Q;
  static vector<int> tmp;
loop:
  int fx = find(x), fy = find(y);
  if (fx ^ fy) {
    if (Gco[fy].count(fx)) {
      if (sz[fy] < sz[fx]) {
        for (auto z : S[fy]) {
          if (Go[z].count(fx)) {
            remove_edge(z, fx);
            ans -= sz[fx];
          }
        }
        swap(x, y);
        swap(fx, fy);
      } else {
        tmp.clear();
        for (auto z : Gi[fx]) {
          if (find(z) == fy) {
            tmp.push_back(z);
            ans -= sz[fx];
          }
        }
        for (auto z : tmp) {
          remove_edge(z, fx);
        }
      }
      for (auto z : S[fx]) {
        S[fy].insert(z);
      }
      ans -= C2(sz[fy]);
      ans -= C2(sz[fx]);
      ans += 1ll * Gi[fy].size() * sz[fx];
      for (auto z : Gi[fx]) {
        if (!Go[z].count(fy)) {
          Q.emplace_back(z, fy); 
        } 
        ans -= sz[fx];
      }
      while (!Gi[fx].empty()) {
        int z = *Gi[fx].begin();
        remove_edge(z, fx);
      }
      Gco[fx].clear();
      uf[fx] = fy;
      sz[fy] += sz[fx];
      ans += C2(sz[fy]);
      for (auto z : S[fx]) {
        tmp.clear();
        for (auto t : Go[z]) {
          Q.emplace_back(z, t);
          tmp.push_back(t);
          ans -= sz[find(t)];
        }
        for (auto t : tmp) {
          remove_edge(z, t);
        }
      }
      S[fx].clear();
    } else if (!Go[x].count(fy)){
      add_edge(x, y);
      ans += sz[fy];
    }
  }
  if (!Q.empty()) {
    x = Q.back().first;
    y = Q.back().second;
    Q.pop_back();
    goto loop;
  }
}

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) {
    uf[i] = i;
    sz[i] = 1;
    S[i].insert(i);
  }
  int x, y;
  while (m--) {
    scanf("%d%d", &x, &y);
    work(x, y);
    printf("%lld
", ans);
  }
  return 0;
}

Problem C 最古の遺跡 3

  首先不难发现问题相当于,从大的值往小的值做扫描线,维护一个堆,每次加入两个位置,弹掉最大的位置。问最后弹出的位置集合等于给定集合的方案数。

  考虑每个位置会剩下的条件,不难发现它是一个类似匹配的东西。考虑从右往左,每遇到一个柱子在小于等于它的高度中匹配一个最高的高度作为它的最终高度。如果匹配不了就不会剩下。

  然后 dp 即可。

  设 $f_{i, j}$ 表示考虑恰好最低的 $j$ 个高度已经被占据的方案数。每次遇到一个剩下的柱子处理它最终匹配 $j + 1$ 的情形,即枚举一下新增的长度。注意到此时只用关心之前的柱子存在匹配的方案数,而不用关心它匹配了什么。

  大概还要预处理另外一个 dp 数组来根据新增的长度计算方案。

Code

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

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int inv(int a, int n) {
  int x, y;
  exgcd(a, n, x, y);
  return (x < 0) ? (x + n) : (x);
}

const int Mod = 1e9 + 7;

template <const int Mod = :: Mod>
class Z {
  public:
    int v;

    Z() : v(0) {	}
    Z(int x) : v(x){	}
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    }
    friend Z operator - (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    }
    friend Z operator * (const Z& a, const Z& b) {
      return Z(a.v * 1ll * b.v);
    }
    friend Z operator ~(const Z& a) {
      return inv(a.v, Mod);
    }
    friend Z operator - (const Z& a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
    friend boolean operator == (const Z& a, const Z& b) {
      return a.v == b.v;
    } 
};

Z<> qpow(Z<> a, int p) {
  Z<> rt = Z<>(1), pa = a;
  for ( ; p; p >>= 1, pa = pa * pa) {
    if (p & 1) {
      rt = rt * pa;
    }
  }
  return rt;
}

typedef Z<> Zi;

const int N = 605, N2 = 1205;

const Zi inv2 ((Mod + 1) >> 1);

int n, n2;
boolean exist[N2];
Zi f[N], g[N][N][2], C[N][N], h[N];

int main() {
  scanf("%d", &n);
  n2 = n << 1;
  for (int i = 1, x; i <= n; i++) {
    scanf("%d", &x);
    exist[x] = true;
  }
  C[0][0] = 1;
  for (int i = 1; i <= n; i++) {
    C[i][0] = C[i][i] = 1;
    for (int j = 1; j < i; j++) {
      C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
    }
  }
  for (int i = 1; i <= n; i++) {
    Zi fac = 1;
    for (int j = 1; j <= i; j++) {
      C[i][j] *= (fac *= j);
    }
  }
  g[1][0][0] = inv2;
  g[1][0][1] = 1;
  for (int i = 2; i <= n; i++) {
    for (int j = 0; j < i; j++) {
      if (j) {
        g[i][j][0] += g[i - 1][j - 1][0] * inv2;
        g[i][j][1] += g[i - 1][j - 1][1] * inv2;
        g[i][j][1] += g[i - 1][j - 1][0];
      }
      g[i][j][0] += g[i - 1][j][0];
      g[i][j][1] += g[i - 1][j][1];
      g[i][j][1] += g[i - 1][j][0];
      g[i][j][0] += g[i - 1][j + 1][0] * inv2;
      g[i][j][1] += g[i - 1][j + 1][1] * inv2;
    }
  }
  f[0] = 1;
  int cnt0 = 0, cnt1 = 0;
  for (int i = n2; i; i--) {
    if (exist[i]) {
      cnt1++;
      for (int j = cnt1; j-- > cnt0; ) {
        for (int l = 1; j + l <= cnt1; l++) {
          f[j + l] += f[j] * g[l][0][1] * C[cnt1 - j - 1][l - 1];
        }  
      }
    } else {
      cnt0++;
      for (int j = 0; j <= cnt1; j++) {
        if (j < cnt0) {
          f[j] = 0;
        } else {
          f[j] *= (j - cnt0 + 1);
        }
      }  
    }
  }
  printf("%d
", f[n].v);
  return 0;
}

Day 3

Problem A 星座 3

   对星空建笛卡尔树,考虑每个矩形内只能选一个点,选了后的转移贡献是笛卡尔树上一段类似链和的东西。

  但好像我的做法比较麻烦,直接对建筑建笛卡尔树,记录选的最高的点的坐标,然后大力启发式合并就行了。

Code

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

const int N = 2e5 + 5;

#define ll long long

template <typename T>
class SparseTable {
	public:
		int n;
		vector<T> Log2;
		vector<vector<T>> f;
		function<boolean(T, T)> compare;

		void init(int n, T* a, const function<boolean(T, T)>& compare = less<T>()) {
			this->n = n;
			this->compare = compare;
			Log2.resize(n + 1);
			Log2[0] = -1;
			for (int i = 1; i <= n; i++) {
				Log2[i] = Log2[i >> 1] + 1;
			}
			f.resize(Log2[n] + 1, vector<T>(n + 1));
			for (int i = 1; i <= n; i++) {
				f[0][i] = a[i];
			}
			for (int i = 1; i <= Log2[n]; i++) {
				for (int j = 1; j + (1 << i) - 1 <= n; j++) {
					T& x = f[i - 1][j], &y = f[i - 1][j + (1 << (i - 1))];
					if (compare(x, y)) {
						f[i][j] = x;
					} else {
						f[i][j] = y;
					}
				}
			}
		}

		T query(int l, int r) {
			int b = Log2[(r - l + 1)];
			T& x = f[b][l], &y = f[b][r - (1 << b) + 1];
			return compare(x, y) ? x : y;
		}
};

typedef class Point {
	public:
		int x, y, w, id;

		Point() {	}
		Point(int x, int y, int w) : x(x), y(y), w(w) {	}

		void read() {
			scanf("%d%d%d", &x, &y, &w);
		}	
		boolean operator < (Point b) const {
			return (x ^ b.x) ? (x < b.x) : (y < b.y); 
		}
		boolean operator == (Point b) const {
			return x == b.x && y == b.y;
		}
} Point;

Point pinf (-N, -N, -1e9);

typedef class SegTreeNode {
	public:
		Point v;
		SegTreeNode *l, *r;
		
		void push_up() {
			if (l->v.y > r->v.y) {
				v = l->v;
			} else {
				v = r->v;
			}
		}
} SegTreeNode;

typedef class SegmentTree {
	public:
		static SegTreeNode pool[N << 1];
		static SegTreeNode* top;

		static SegTreeNode* newnode() {
			top->v = pinf;
			return top++;
		}
		
		int n;
		SegTreeNode* rt;

		SegmentTree() : rt(NULL) {	}
		
		void build(SegTreeNode*& p, int l, int r, Point* ps) {
			p = newnode();
			if (l ^ r) {
				int mid = (l + r) >> 1;
				build(p->l, l, mid, ps);
				build(p->r, mid + 1, r, ps);
				p->push_up();
			} else {
				p->v = ps[l];
			}
		}
		void build(int n, Point* ps) {
			this->n = n;
			build(rt, 1, n, ps);
		}

		Point query(SegTreeNode* p, int l, int r, int ql, int qr) {
			if (l == ql && r == qr) {
				return p->v;
			}
			int mid = (l + r) >> 1;
			if (qr <= mid) {
				return query(p->l, l, mid, ql, qr);
			} else if (ql > mid) {
				return query(p->r, mid + 1, r, ql, qr);
			}
			Point a = query(p->l, l, mid, ql, mid);
			Point b = query(p->r, mid + 1, r, mid + 1, qr);
			return (a.y > b.y) ? (a) : (b);
		}

		void modify(SegTreeNode* p, int l, int r, int idx, Point v) {
			if (l == r) {
				p->v = v;
				return;
			}
			int mid = (l + r) >> 1;
			if (idx <= mid) {
				modify(p->l, l, mid, idx, v);
			} else {
				modify(p->r, mid + 1, r, idx, v);
			}
			p->push_up();
		}

		Point query(int l, int r) {
			return query(rt, 1, n, l, r);
		}
		void modify(int idx, Point v) {
			return modify(rt, 1, n, idx, v);
		}
} SegmentTree;

SegTreeNode SegmentTree :: pool[N << 1];
SegTreeNode* SegmentTree :: top = SegmentTree :: pool;

typedef class Fenwick {
	public:
		int n;
		ll* a;

		void init(int n) {
			this->n = n;
			a = new ll[(n + 1)];
			fill(a, a + n + 1, 0);
		}
		void add(int idx, ll v) {
			for ( ; idx <= n; idx += (idx & (-idx))) {
				a[idx] += v;
			}
		}
		void add(int l, int r, ll v) {
			add(l, v);
			add(r + 1, -v);
		}
		ll query(int idx) {
			ll rt = 0;
			for ( ; idx; idx -= (idx & (-idx)))
				rt += a[idx];
			return rt;
		}
} Fenwick;

template <typename T>
boolean vmax(T& a, T b) {
	return (a < b) ? (a = b, true) : false;
}

int n, m;
int A[N];
Point Ps[N];
SegmentTree st;
SparseTable<int> sta;

int get_ql(int L) {
	int l = 1, r = m, mid;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (Ps[mid].x >= L) {
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	return r + 1;
}

ll f[N];
Fenwick fen;
int dfs(int L, int R) {
	if (L > R)
		return 0;
	int mid = sta.query(L, R);
	int D = A[mid];
	int ql = get_ql(L);
	int qr = get_ql(R + 1) - 1;
	vector<Point> ps;
	if (ql <= qr) {
		Point P;
		while ((P = st.query(ql, qr)).y > D) {
			st.modify(P.id, pinf);
			ps.push_back(P);
		}
	}
	int ls = dfs(L, mid - 1);
	int rs = dfs(mid + 1, R);
	if (ls)
		fen.add(L, mid - 1, f[rs]);
	if (rs)
		fen.add(mid + 1, R, f[ls]);
	f[mid] = f[ls] + f[rs];
	fen.add(mid, mid, f[mid]);
	for (auto p : ps)
		vmax(f[mid], p.w + fen.query(p.x));
	return mid;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", A + i);
	}
	int* tmp = new int[(n + 1)];
	for (int i = 1; i <= n; i++)
		tmp[i] = i;
	sta.init(n, tmp, [&] (int x, int y) {	return A[x] > A[y];	});
	scanf("%d", &m);
	ll sum = 0;
	for (int i = 1; i <= m; i++) {
		Ps[i].read();
		sum += Ps[i].w;
	}
	sort(Ps + 1, Ps + m + 1);
	for (int i = 1; i <= m; i++) {
		Ps[i].id = i;
	}
	st.build(m, Ps);
	fen.init(n);
	int rt = dfs(1, n);
	printf("%lld
", sum - f[rt]);
	return 0;
}

Problem B 収穫

  考虑一棵树被 $i$ 收获后下一个人是确定的。

  然后大概就有一个基环树森林。

  对于树的部分是个简单扫描线。

  对于环的部分,强行让开始的地方是环上某个特定的点,这一部分也是个简单扫描线。

  因为不会注意炸 int 和 long long 又被这道题教育了。

Code

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

#define ll long long
#define pii pair<int, int>

const int N = 2e5 + 5;

typedef class Fenwick {
  public:
    int n;
    int* a;
    vector<pii> opt;

    void init(int n) {
      this->n = n;
      a = new int[(n + 1)];
      fill(a, a + n + 1, 0);
    }
    void add(int idx, int val, bool record = true) {
      record && (opt.emplace_back(idx, val), 1);
      for ( ; idx <= n; a[idx] += val, idx += (idx & (-idx)));
    }
    int query(int idx) {
      int rt = 0;
      for (idx = min(idx, n); idx; rt += a[idx], idx -= (idx & (-idx)));
      return rt;
    }
    void reset() {
      for (auto t : opt) {
        add(t.first, -t.second, false);
      }
      opt.clear();
    }
    void clear() {
      opt.clear();
      delete[] a;
    }
} Fenwick;

typedef class Event {
  public:
    ll T;
    int id;

    Event(ll T, int id) : T(T), id(id) { }

    bool operator < (Event b) const {
      return (T ^ b.T) ? (T < b.T) : (id < b.id);
    }
} Event;

int n, m, L, C, q;
int A[N], B[N << 1];
int g[N], gt[N];
int vis[N];
bool inc[N];

int stk[N];
ll dep[N];
vector<int> Cs;
vector<pii> G[N];

ll ans[N];

void dfs1(int p, int fa, int s) {
  vis[p] = 1;
  for (auto _ : G[p]) {
    int e = _.first;
    int w = _.second;
    if ((e ^ fa) && (e ^ s)) {
      dep[e] = dep[p] + w;
      dfs1(e, p, s); 
    }
  }
}

vector<ll> vd;
vector<Event> Ge[N];
void prepare_event() {
  int p = m - 1, L = 0, R = 0;
  while (L < n || R < m) {
    if (R == m || (L < n && A[L] < B[R])) {
      Ge[p].emplace_back(dep[p] + (A[L] + ::L - B[p]) % ::L, 0);
      vd.push_back(Ge[p].back().T);
      L++;
    } else {
      p = R++;
    }
  }
  scanf("%d", &q);
  ll t;
  for (int i = 1, x; i <= q; i++) {
    scanf("%d%lld", &x, &t);
    t += dep[--x];
    Ge[x].emplace_back(t, i);
    vd.push_back(t);
  }
  sort(vd.begin(), vd.end());
}

int gett(ll t) {
  return lower_bound(vd.begin(), vd.end(), t) - vd.begin() + 1;
}

ll D;
ll d[N];
Fenwick fen;
vector<ll> vr;
vector<Event> Ec;
void dfs2(int p, int fa, int s) {
  if (p ^ s) {
    for (auto e : Ge[p]) {
      if (e.id) {
        ans[e.id] -= fen.query(gett(e.T));
      }
    }
  }
  for (auto e : Ge[p]) {
    if (!e.id) {
      fen.add(gett(e.T), 1);
    }
  }
  for (auto _ : G[p]) {
    int e = _.first;
    if ((e ^ fa) && (e ^ s)) {
      dfs2(e, p, s);
    }
  }
  for (auto e : Ge[p]) {
    if (e.id) {
      if (p ^ s) {
        ans[e.id] += fen.query(gett(e.T));
      }
      if (inc[p]) {
        ll nt = e.T - dep[p] - d[p] + D;
        if (nt >= 0) {
          Ec.emplace_back(nt, e.id);
        }
      }
    } else {
      Ec.emplace_back(e);
      vr.push_back(e.T % D);
    }
  }
}

Fenwick fen2;
void solve(int cs) {
  Ec.clear();
  vr.clear();
  int p = g[cs];
  D = gt[cs];
  while (p ^ cs) {
    d[p] = D;
    D += gt[p];
    p = g[p];
  }
  dfs2(cs, -1, cs);
  fen.reset();
  sort(Ec.begin(), Ec.end());
  sort(vr.begin(), vr.end());
  fen2.init(vr.size());
  int cnt = 0;
  __int128 sums = 0;
  for (auto e : Ec) {
    if (e.id) {
      int dt = upper_bound(vr.begin(), vr.end(), e.T % D) - vr.begin();
      int cr = cnt - fen2.query(dt);
      ll dlt = ((__int128) cnt) * (e.T / D) - sums - cr;
      ans[e.id] += dlt;
    } else {
      int dt = lower_bound(vr.begin(), vr.end(), e.T % D) - vr.begin() + 1;
      cnt++;
      sums += e.T / D;
      fen2.add(dt, 1);
    }
  }
  fen2.clear();
}

int main() {
  scanf("%d%d%d%d", &m, &n, &L, &C);
  for (int i = 0; i < m; i++) {
    scanf("%d", B + i);
    B[i + m] = B[i] + L;
  }
  for (int i = 0; i < n; i++) {
    scanf("%d", A + i);
  }
  A[n] = A[0] + L;
  for (int i = 0; i < m; i++) {
    g[i] = upper_bound(B, B + (m << 1), B[i] + L - (C % L)) - B - 1;
    gt[i] = (B[i] + L - (C % L)) - B[g[i]] + C;
    (g[i] >= m) && (g[i] -= m);
    G[g[i]].emplace_back(i, gt[i]);
  }
  fill(vis, vis + m, -1);
  for (int i = 0; i < m; i++) {
    int tp = 0, p = i;
    while (!~vis[p]) {
      vis[p] = i;
      stk[++tp] = p;
      p = g[p];
    }
    if (vis[p] == i) {
      int q;
      do {
        q = stk[tp--];
        inc[q] = true;
      } while (q ^ p);
    }
  }
  fill(vis, vis + m, 0);
  for (int i = 0; i < m; i++) {
    if (!vis[i] && inc[i]) {
      dfs1(i, -1, i);
      Cs.push_back(i);
    }
  }
  prepare_event();
  fen.init(vd.size());
  for (auto x : Cs) {
    solve(x);
  }
  for (int i = 1; i <= q; i++) {
    printf("%lld
", ans[i]);
  }
  return 0;
}

Problem C 迷い猫

  $A geqslant 3$ 的情况,先求出每个点到 0 的最短路,每条边按照较近的一端的距离模 3 染色就行了。不难发现你总能区分出距离会减小的一些边。

  当 $A = 2$ 的时候,上面的做法会有点问题。因为在链上时无法区分。

  按 $110100$ 为询问在链上进行编码。如果遇到 $1101$ 或者 $1010$ 就能识别出方向。$101$ 下一个接 $1$ 表示正确方向否则表示错误方向

  然后手动把走了三步并且是错误方向的判掉。

Code

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

namespace {


}  // namespace

std::vector<int> Mark(int N, int M, int A, int B, std::vector<int> U, std::vector<int> V) {
  vector<int> rt (M, 0);
  vector<vector<int>> G (N);
  for (int i = 0; i < M; i++) {
    G[U[i]].push_back(V[i]);
    G[V[i]].push_back(U[i]);
  }
  if (A >= 3) {
    vector<int> dep (N, -1);
    queue<int> Q;
    Q.push(0);
    dep[0] = 0;
    while (!Q.empty()) {
      int p = Q.front();
      Q.pop();
      for (auto e : G[p]) {
        if (dep[e] == -1) {
          dep[e] = dep[p] + 1;
          Q.push(e);
        }
      }
    }
    for (int i = 0; i < M; i++) {
      int x = U[i], y = V[i];
      if (dep[x] > dep[y]) {
        swap(x, y);
      }
      rt[i] = dep[x] % 3;
    }
  } else {
    vector<int> col (N, 0);
    vector<int> dep (N, 0);
    function<void(int, int, int)> dfs = [&] (int p, int fa, int cdis) {
      static char str[] = "110100";
      if (p) {
        cdis = (G[fa].size() == 2u) ? (cdis % 6 + 1) : (0);
        col[p] = (!cdis) ? (col[fa] ^ 1) : (str[cdis - 1] - '0');
        dep[p] = dep[fa] + 1;
      }
      for (auto e : G[p]) {
        if (e ^ fa) {
          dfs(e, p, cdis);
        }
      }
    };
    dfs(0, -1, 0);
    for (int i = 0; i < M; i++) {
      int x = U[i], y = V[i];
      if (dep[x] > dep[y]) {
        swap(x, y);
      }
      rt[i] = col[y];
//      cerr << x << " " << y << " " << rt[i] << '
';
    }
  }
  return rt;
}
#include <bits/stdc++.h>
#include "Catherine.h"
using namespace std;

namespace {

  int A, B;
  
  bool has_dir;
  int last_col;
  vector<int> seq;

  bool match(const char* s) {
    int len = strlen(s);
    if (len > seq.size()) {
      return false;
    }
    int t = seq.size();
    while (len-- && t--) {
      if (s[len] - '0' != seq[t]) {
        return false;
      }
    }
    return true;
  }

}  // namespace

void Init(int A, int B) {
  ::A = A;
  ::B = B;
  has_dir = false;
  last_col = -1;
}

int Move(std::vector<int> y) {
  if (A >= 3) {
    const static int go[3] = {1, 2, 0};
    vector<int> id;
    for (int i = 0; i < A; i++) {
      if (y[i]) {
        id.push_back(i);
      }
    }
    int sz = id.size();
    assert(sz <= 2 && sz >= 1);
    if (sz == 1) {
      return id[0]; 
    } else {
      return go[3 ^ id[0] ^ id[1]];
    }
  } else {
    if (has_dir) {
      if (!y[0] + !y[1] == 1) {
        return last_col = !y[0];
      } else {
        return last_col = last_col ^ 1;
      }
    } else {
      int sum = last_col != -1;
      for (auto x : y) {
        sum += x;
      }
      if (sum > 2) {
        has_dir = true;
        if (last_col == -1) {
          return last_col = y[0] > y[1];
        } else {
          if (!y[last_col]) {
            return -1;
          } else {
            return last_col = last_col ^ 1;
          }
        }
      } else if (sum == 1) {
        has_dir = true;
        if (last_col == -1) {
          return last_col = !y[0];
        } else {
          return -1;
        }
      } else {
        if (last_col == -1) {
          if (y[0] == 2) {
            last_col = 0;
            seq = vector<int>{0, 0};
          } else if (y[1] == 2) {
            last_col = 1;
            seq = vector<int>{1, 1};
          } else {
            last_col = 1;
            seq = vector<int>{0, 1};
          }
          return last_col;
        } else {
          seq.push_back(!y[0]);
          int sz = seq.size();
          if (sz >= 4) {
            if (seq[sz - 2] == 1 && seq[sz - 3] == 0 && seq[sz - 4] == 1) {
              has_dir = true;
              if (seq[sz - 1] == 0) {
                return -1;
              }
            } else if (seq[sz - 1] == 1 && seq[sz - 2] == 0 && seq[sz - 3] == 1) {
              has_dir = true;
              if (seq[sz - 4] == 1) {
                return -1;
              }
            } else if (match("00110") || match("01001")) {
              has_dir = true;
              return -1;
            } 
          }
          return last_col = !y[0];
        }
      }
    }
  }
  return -1;
}

Day 4

Problem A 首都

  模拟。

  倍增优化建图。

Code

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

typedef class Edge {
  public:
    int ed, nx;

    Edge(int ed, int nx) : ed(ed), nx(nx) { }
} Edge;

typedef class MapManager {
  public:
    vector<int> h;
    vector<Edge> E;

    void init(int n) {
      h.resize(n + 1, -1);
    }
    void add_edge(int u, int v) {
      E.emplace_back(v, h[u]);
      h[u] = (signed) E.size() - 1;
    }
    Edge& operator [] (int p) {
      return E[p];
    }
} MapManager;

const int Nmx = 2e5 + 5, bzmax = 19;

int n, m, N;
vector<int> Tr[Nmx];
vector<int> city[Nmx];

int ans;
int in[Nmx], dep[Nmx];
int bz[Nmx][bzmax], id[Nmx][bzmax];

MapManager G;
stack<int> stk;
vector<bool> ins;
vector<int> dfn, low;
void tarjan(int p) {
  static int dfc = 0;
  static vector<int> tmp;
  dfn[p] = low[p] = ++dfc;
  ins[p] = true;
  stk.push(p);
  for (int i = G.h[p], e; ~i && (e = G[i].ed, 1); i = G[i].nx) {
    if (!dfn[e]) {
      tarjan(e);
      low[p] = min(low[p], low[e]);
    } else if (ins[e]) {
      low[p] = min(low[p], dfn[e]);
    }
  }
  if (dfn[p] == low[p]) {
    int q = -1;
    int sum = 0;
    bool has_out = false;
    tmp.clear();
    do {
      q = stk.top();
      stk.pop();
      low[q] = low[p];
      sum += q <= m;
      tmp.push_back(q);
    } while (q ^ p);
    for (auto q : tmp) {
      for (int i = G.h[q], e; ~i && (e = G[i].ed, 1); i = G[i].nx) {
        if (low[e] ^ low[p]) {
          has_out = true;
        }
      }
    }
    if (!has_out) {
      ans = min(ans, sum);
    }
  }
}
void do_tarjan(int N) {
  dfn.resize(N + 1, 0);
  low.resize(N + 1, 0);
  ins.resize(N + 1, 0);
  for (int i = 1; i <= N; i++) {
    if (!dfn[i]) {
      tarjan(i);
    }
  }
}

void dfs(int p, int fa) {
  static int dfc = 0;
  in[p] = ++dfc;
  dep[p] = dep[fa] + 1;
  bz[p][0] = fa;
  id[p][0] = ++N;
  for (int i = 1; i < bzmax; i++) {
    bz[p][i] = bz[bz[p][i - 1]][i - 1];
    id[p][i] = ++N;
  }
  for (auto e : Tr[p]) {
    if (e ^ fa) {
      dfs(e, p);
    }
  }
}

int jump(int u, int dif) {
  for (int i = 0; dif; i++, dif >>= 1) {
    if (dif & 1) {
      u = bz[u][i];
    }
  }
  return u;  
}

int lca(int u, int v) {
  if (dep[u] < dep[v]) {
    swap(u, v);
  }
  u = jump(u, dep[u] - dep[v]);
  if (u == v) {
    return u;
  }
  for (int i = bzmax, x, y; i--; ) {
    x = bz[u][i];
    y = bz[v][i];
    if (x ^ y) {
      u = x, v = y;
    } 
  }
  return bz[u][0];
}

void link(int u, int d) {
  int lg2 = 0;
  while ((1 << (lg2 + 1)) <= d)
    lg2++;
  G.add_edge(id[u][0], id[u][lg2]);
  G.add_edge(id[u][0], id[jump(u, d - (1 << lg2))][lg2]);
}

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1, u, v; i < n; i++) {
    scanf("%d%d", &u, &v);
    Tr[u].push_back(v);
    Tr[v].push_back(u);
  }
  for (int i = 1, x; i <= n; i++) {
    scanf("%d", &x);
    city[x].push_back(i);
  }
  N = m;
  dfs(1, 0);
  G.init(N);
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j < bzmax; j++) {
      G.add_edge(id[i][j], id[i][j - 1]);
      if (bz[i][j - 1]) {
        G.add_edge(id[i][j], id[bz[i][j - 1]][j - 1]);
      }
    }
  }
  for (int i = 1; i <= m; i++) {
    auto& v = city[i];
    int L = v[0], R = v[0];
    for (auto p : v) {
      G.add_edge(id[p][0], i);
      G.add_edge(i, id[p][0]);
      if (in[p] < in[L]) {
        L = p;
      }
      if (in[p] > in[R]) {
        R = p;
      }
    }
    int g = lca(L, R);
    for (auto p : v) {
      link(p, dep[p] - dep[g] + 1);
    }
  }
  ans = m + 1;
  do_tarjan(N);
  printf("%d
", ans - 1);
  return 0;
}

Problem B 伝説の団子職人

咕咕咕

Problem C 治療計画

  按右端点从小到大考虑每个治疗方案,一个治疗方案有意义当且仅当改变了最终会延伸到最左边的病毒的起始位置。

  不难发现这个条件是 $L_i leqslant R_j + 1 - |T_i - T_j|$,$R_j leqslant R_i$。

  但是发现后一个限制不满足的时候没有影响,讨论 $T_i, T_j$ 大小,主席树优化建图即可。

Code

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

#define ll long long

const ll llf = (signed ll) (~0ull >> 3);

template <typename T>
vector<T> discrete(T* a, int* b, int n) {
  vector<T> tmp (a + 1, a + n + 1);
  sort(tmp.begin(), tmp.end());
  tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());
  for (int i = 1; i <= n; i++) {
    b[i] = lower_bound(tmp.begin(), tmp.end(), a[i]) - tmp.begin() + 1;
  }
  return tmp;
}

const int N = 1e5 + 5;
const int M = N * 42;

typedef class Treatment {
  public:
    int t, l, r, c;
    
    void read() {
      scanf("%d%d%d%d", &t, &l, &r, &c);
    }
    bool operator < (Treatment b) const {
      return t < b.t;
    }
} Treatment;

typedef class Node {
  public:
    int p;
    ll d;

    Node(int p, ll d) : p(p), d(d) {  }

    bool operator < (Node b) const {
      return d > b.d;
    }
} Node;

int L, n, m;
ll f[M];
Treatment tr[N];
int ls[M], rs[M], id[M], bk[M];

#define pi(x) ((x << 1) - 1)
#define po(x) (x << 1)

void insert(int op, int& p, int l, int r, int idx, int x) {
  p = ++m;
  ls[p] = ls[op];
  rs[p] = rs[op];
  if (l == r) {
    bk[p] = op;
    id[p] = pi(x);
    return;
  }
  int mid = (l + r) >> 1;
  if (idx <= mid) {
    insert(ls[op], ls[p], l, mid, idx, x);
  } else {
    insert(rs[op], rs[p], mid + 1, r, idx, x);
  }
}

int lmi[N], lmx[N];
int lrt[N], rrt[N];
int main() {
  scanf("%d%d", &L, &n);
  m = n << 1;
  for (int i = 1; i <= n; i++) {
    tr[i].read();
  }
  sort(tr + 1, tr + n + 1);
  for (int i = 1; i <= n; i++) {
    lmi[i] = tr[i].r - tr[i].t + 1;
    lmx[i] = tr[i].r + tr[i].t + 1;
  }
  auto vlmi = discrete(lmi, lmi, n);
  auto vlmx = discrete(lmx, lmx, n);
  for (int i = 1; i <= n; i++) {
    int l = lower_bound(vlmi.begin(), vlmi.end(), tr[i].l - tr[i].t) - vlmi.begin() + 1;
    if (l <= n) {
      insert(lrt[i - 1], lrt[i], 1, n, l, i);
    } else {
      lrt[i] = lrt[i - 1];
    }
  }
  for (int i = n; i; i--) {
    int l = lower_bound(vlmx.begin(), vlmx.end(), tr[i].l + tr[i].t) - vlmx.begin() + 1;
    if (l <= n) {
      insert(rrt[i + 1], rrt[i], 1, n, l, i);
    } else {
      rrt[i] = rrt[i + 1];
    }
  }
  for (int i = 1; i <= m; i++) {
    f[i] = llf;
  }
  priority_queue<Node> Q;
  for (int i = 1; i <= n; i++) {
    if (tr[i].l == 1) {
      Q.push(Node(po(i), f[po(i)] = tr[i].c));
    }
  }
  auto upd = [&] (int e, ll d) {
    if (d < f[e]) {
      Q.push(Node(e, f[e] = d));
    }
  };
  function<void(int, int, int, int, ll)> qry = [&] (int p, int l, int r, int idx, ll d) {
    if (l == r) {
      upd(p, d);
      return;
    }
    int mid = (l + r) >> 1;
    if (idx <= mid) {
      qry(ls[p], l, mid, idx, d);
    } else {
      upd(ls[p], d);
      qry(rs[p], mid + 1, r, idx, d);
    }
  };
  while (!Q.empty()) {
    int p = Q.top().p;
    ll fc = Q.top().d;
    Q.pop();
    if (fc != f[p]) {
      continue;
    }
    if (p <= (n << 1)) {
      if (p & 1) {
        upd(p + 1, fc + tr[(p + 1) >> 1].c);
      } else {
        int t = p >> 1;
        qry(lrt[t], 1, n, lmi[t], fc);
        qry(rrt[t], 1, n, lmx[t], fc);
      }
    } else {
      if (ls[p]) {
        upd(ls[p], fc);
      }
      if (rs[p]) {
        upd(rs[p], fc);
      }
      if (bk[p]) {
        upd(bk[p], fc);
      }
      if (id[p]) {
        upd(id[p], fc);
      }
    }
  }
  ll ans = llf;
  for (int i = 1; i <= n; i++) {
    if (tr[i].r == L) {
      ans = min(ans, f[po(i)]);
    }
  }
  if (ans == llf) {
    puts("-1");
  } else {
    printf("%lld
", ans);
  }
  return 0;
}

原文地址:https://www.cnblogs.com/yyf0309/p/joisc2020.html