CEOI 2022 Day 1 题解

好像题都不难,可惜我 C 做了 3h,主要格式错了,是必须按照他的格式!!先读入才能输出。

A. Abracadabra

考虑归并排序有这样一个性质,考虑 \(a_i > a_{i+1}\),那么一旦 \(a_i\) 被扔进去,\(a_{i+1}\) 会紧跟着被扔进去。

那么可以将 \(a\) 按照前缀 \(\max\) 分段,每个前缀 \(\max\) 为开头,管辖后面一段 \(<\) 它的。

这样每次归并相当于就是对着这个前缀 \(\max\) 做了一次排序,段内不变。

考虑每次相当于是从 \(n / 2\) 处劈开,然后后半部分新生成了几个前缀 \(\max\)

我们考虑 \(i\) 一旦是前缀 \(\max\) 一直都是前缀 \(\max\),所以这个过程不到 \(n\) 次其实也就不会变了,所以每次可以暴力找到新生成的前缀 \(\max\),然后插入到当前序列中。

仔细考虑一下事实上就是以前缀 \(\max\) 为值排序,可以开一颗权值线段树,然后 \(i\) 位置存如果 \(i\) 是前缀 \(\max\) 的段长度。然后每次可以找 \(n / 2\) 所在的段,如果不是最后一个就暴力分裂,每次找一些新段可以 \(\log\) 或者 \(O(1)\)(好像单调栈预处理一下就好了,可惜我 sb 写了个 st 表)。

时间复杂度 \(O(n \log n + q \log n)\)

// Skyqwq
#include <bits/stdc++.h>

#define pb push_back
#define fi first
#define se second
#define mp make_pair

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }

template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N = 2e5 + 5, M = 1e6 + 5, L = 18;

int n, q, a[N], la[N], pos[N];

int sz[N];

int st[L][N], g[N];

void bdST() {
	g[0] = -1;
	for (int i = 1; i <= n; i++) st[0][i] = a[i], g[i] = g[i >> 1] + 1;
	for (int j = 1; j <= g[n]; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			st[j][i] = max(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
		}
	}
}

int qry(int l, int r) {
	int k = g[r - l + 1];
	return max(st[k][l], st[k][r - (1 << k) + 1]);
}



int b[N];

int dat[N << 2];

#define ls (p << 1)
#define rs (p << 1 | 1)

void pu(int p) {
	dat[p] = dat[ls] + dat[rs];
}

int ask(int p, int l, int r, int k) {
	if (l == r) {
		return a[pos[r] + k - 1];
	}
	int mid = (l + r) >> 1;
	if (k <= dat[ls]) return ask(ls, l, mid, k);
	else return ask(rs, mid + 1, r, k - dat[ls]);
}

void chg(int p, int l, int r, int x, int y) {
	if (l == r) {
		dat[p] = sz[r];
		return;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) chg(ls, l, mid, x, y);
	else chg(rs, mid + 1, r, x, y);
	pu(p);
}

void upd(int x, int y) {
	sz[x] = y;
	chg(1, 1, n, x, y);
}

void fd(int x, int y) {
	for (int i = x; i <= y; i++) {
		int l = i, r = y;
		while (l < r) {
			int mid = (l + r + 1) >> 1;
			if (qry(i, mid) == a[i]) l = mid;
			else r = mid  - 1;
		}
		upd(a[i], r - i + 1);
		i = r;
	}
}

bool div(int p, int l, int r, int k) {
	if (l == r) {
		if (sz[r] == k) {
			return 0;
		} else {
			fd(pos[r] + k, pos[r] + sz[r] - 1);
			upd(r, k);
			return 1;
		}
	}
	int mid = (l + r) >> 1;
	if (k <= dat[ls]) return div(ls, l, mid, k);
	else return div(rs, mid + 1, r, k - dat[ls]);
}


struct E{
	int t, i, id;
	bool operator < (const E &b) const {
		return t < b.t;
	}
} e[M];

int ans[M], now;

void sh(int x) {
	while (now <= q && e[now].t <= x) {
		ans[e[now].id] = ask(1, 1, n, e[now].i);
		now++;
	}
}

void inline out() {
	for (int i = 1; i <= n; i++) cout << ask(1, 1, n, i) << " "; cout << endl;
}

int main() {
    read(n), read(q);
    for (int i = 1; i <= n; i++) read(a[i]), pos[a[i]] = i; 
    bdST(); fd(1, n);
    for (int i = 1; i <= q; i++) {
    	read(e[i].t), read(e[i].i), e[i].id = i;
    }
    sort(e + 1, e + 1 + q);
    int c = 0; now = 1;
    sh(c); //out();
	while (1) {
		if (!div(1, 1, n, n / 2)) break;
		sh(++c); //out();
	}
	sh(1e9);
	for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
    return 0;
}

B. Homework

考虑建出表达式树,考虑 \(i\) 能不能成为答案,枚举 \(i\) 所在 \(?\) 的位置,把值离散化成 \(0, 1, 2\) 分别表示 \(<i\)\(=i\)\(>i\)。然后 \(i\) 从叶子往跟走,如果遇到 \(\max\),那么另一边儿子必须是 \(0\),如果是 \(\min\) 就得是 \(2\)

所以考虑 \(f_{u, i}\) 表示 \(u\) 子树算出来答案是 \(i\),只填 \(0 / 2\),需要 \(0\) 的个数的集合,可以证明这个集合是个区间。证明:具体考虑转移的时候,归纳儿子是区间,比如 \(\max\) 时,这里是 \(0\) 就是俩都是 \(0\),算出来是俩区间的叠加也是区间;如果是 \(1\) 就是枚举那边是 \(2\) 然后区间往右平移另一边的 \(?\) 大小,讨论一下发现俩区间肯定有交(比如这俩子树 \(?\) 大小分别是 \(A, B\),俩区间分别是 \([x, y + B], [u, v + A]\),那么左边的区间肯定过 \(A\),如果 \(v < A\) 现在肯定过 \(A\),如果原来 \(u>A\) 那也不超过 \(B\),肯定有交。),那么并也是区间了。

那么考虑 \(h_i\) 是从根到 \(i\) 这个位置的问号路上的 \(f\) 叠加起来的结果,如果 \(i - 1\) 属于这个区间那么 \(i\) 就是可行的,区间覆盖一下就行了。

复杂度 \(O(n)\)

// Skyqwq
#include <bits/stdc++.h>

#define pb push_back
#define fi first
#define se second
#define mp make_pair

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }

template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N = 2e6 + 5, INF = 1e9;

string str;

vector<int> g[N];

int idx, op[N], s[N], top, n, tot, rt;

PII I = mp(INF, -INF);

PII inline mg(PII a, PII b) {
	return mp(min(a.fi, b.fi), max(a.se, b.se));
}

PII inline add(PII a, PII b) {
	return mp(a.fi + b.fi, a.se + b.se);
}

PII inline apd(PII a, int b) {
	return mp(a.fi, a.se + b);
}

PII f[N][2];

int sz[N];

void dfs1(int u) {
	sz[u] = u <= n ? 1 : 0;
	if (u <= n) {
		f[u][0] = mp(1, 1);
		f[u][1] = mp(0, 0);
		return;
	}
	for (int v: g[u]) {
		dfs1(v);
		sz[u] += sz[v];
	}
	int A = g[u][0], B = g[u][1];
	if (op[u] == 0) {
		f[u][0] = mg(apd(f[A][0], sz[B]), apd(f[B][0], sz[A]));
		f[u][1] = add(f[A][1], f[B][1]);
	} else {
		f[u][1] = mg(apd(f[A][1], sz[B]), apd(f[B][1], sz[A]));
		f[u][0] = add(f[A][0], f[B][0]);
	}
	// cerr << u << " " << A << " " << B << " ----:::\n";
	// for (int i = 0; i < 2; i++) {
	// 	cerr << f[u][i].fi << " " << f[u][i].se << " ---\n";
	// }
}

PII ret = I;

int c[N];

void dfs2(int u, PII now) {
	if (u <= n) {
		c[now.fi]++, c[now.se + 1]--;
		return;
	}
	for (int i = 0; i < 2; i++) {
		dfs2(g[u][i], add(now, f[g[u][i ^ 1]][op[u] == 0 ? 1 : 0]));
	}
}	

int main() {
    cin >> str;
    for (char c: str) if (c == '?') ++n; idx = n;
    rt = idx + 1;
    for (int i = 0; i < str.size(); i++) {
    	if (str[i] == '(') {
    		s[++top] = ++idx;
    		if (top > 1) g[s[top - 1]].pb(s[top]);
    		op[idx] = str[i - 1] == 'n' ? 0 : 1;
    	} else if (str[i] == ')') {
    		--top;
    	} else if (str[i] == '?') {
    		++tot;
    		assert(top);
    		g[s[top]].pb(tot);
    	}
    }
    dfs1(rt);
    dfs2(rt, mp(0, 0));
    int ans = 0;
    //cerr << ret.fi << " -- " << ret.se << endl;
    for (int i = 0; i < n; i++) {
    	if (i) c[i] += c[i - 1];
    	if (c[i]) ans++;
    }
    printf("%d\n", ans);
    return 0;
}

C. Prize

考虑就选树 \(1\)\(dfs\) 序的前 \(K\) 个作为集合,这样树 \(1\) 的虚树没有其他点!

然后你问的问题可以还原这棵树相当于是连边 \((l, a), (l,b)\) ,只有虚树构成的点的图联通。

考虑暴力一点把 \(2\) 的虚树每条边都问了,这样就是 \(2K - 2\) 了。

然后你发现你没有利用上还能知道到 \(l\) 深度这件事。

考虑 dfn 排序建虚树的那个过程,会调用 \(K - 1\)\(lca\),事实上你只要把调用的 \(lca(a, b)\) 当做询问 \(a, b\) 就行了(就是相邻 \(dfn\))!

你考虑在树 \(2\) 上,归纳之前的边是知道的,然后你问了相邻两个 \(dfn\)\(lca\),如果他们祖先关系也行,如果形成的新的虚点,这俩点也向虚点连边了!在树 \(1\) 上,考虑每次至少是有 \(i\) 向之前的点连边,这样那个图也是联通的!

// Skyqwq
#include <bits/stdc++.h>

#define pb push_back
#define fi first
#define se second
#define mp make_pair

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }

// char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;

// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)

template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N = 1e6 + 5;


vector<PII> t;
struct G{
	vector<int> g[N];
	int sz[N], fa[N], dep[N], top[N], hson[N], dfn[N], dfncnt, pre[N];
	int rt;
	void dfs1(int u) {
	    sz[u] = 1;
	    for (int v: g[u]) {
	        if (v == fa[u]) continue;
	        dep[v] = dep[u] + 1, fa[v] = u;
	        dfs1(v);
	        sz[u] += sz[v];
	        if (sz[v] > sz[hson[u]]) hson[u] = v;
	    }
	}

	void dfs2(int u, int tp) {
	    top[u] = tp; dfn[u] = ++dfncnt;
	    pre[dfn[u]] = u;
	    if (hson[u]) dfs2(hson[u], tp);
	    for (int v: g[u]) {
	        if (v == fa[u] || v == hson[u]) continue;
	        dfs2(v, v);
	    }
	}

	int lca(int x, int y) {
	    while (top[x] != top[y]) {
	        if (dep[top[x]] < dep[top[y]]) swap(x, y);
	        x = fa[top[x]];
	    }
	    if (dep[x] < dep[y]) swap(x, y);
	    return y;
	}
	int s[N], tp;
	vector<int> e[N];
	void insert(int x) {
	    if (!tp) { s[++tp] = x; return; }
	    int p = lca(x, s[tp]);
	    t.pb(mp(x, s[tp]));
	    while (tp > 1 && dep[s[tp - 1]] >= dep[p]) e[s[tp - 1]].pb(s[tp]), tp--;
	    if (s[tp] != p) {
	        e[p].pb(s[tp]);
	        s[tp] = p;
	    }
	    s[++tp] = x;
	}
	int inline build(vector<int> &A) {
	    tp = 0;
	    sort(A.begin(), A.end(), [&] (int x, int y) {return dfn[x] < dfn[y]; });
	    for (int x: A) {
	        insert(x);
	    }
	    for (int i = 1; i < tp; i++)
	        e[s[i]].pb(s[i + 1]);
	    return s[1];
	}
	void init () {
		dfs1(rt);
		dfs2(rt, rt);
	}
	void add(int x, int y, int z) {
		h[x].pb(mp(y, z));
		h[y].pb(mp(x, -z));
	}
	vector<PII> h[N];
	bool vis[N];
	LL d[N];
	void dfs4(int u) {
		vis[u] = 1;
		for (PII o: h[u]) {
			int v = o.fi, w = o.se;
			if (!vis[v]) {
				d[v] = d[u] + o.se;
				dfs4(v);
			}
		}
	}
	void bd(int u) {
		dfs4(u);
	}
	int D(int x, int y) {
		int p = lca(x, y);
		assert(vis[p] && vis[x] && vis[y]);
		return (LL)d[x] + d[y] - 2 * d[p];
	}
} t1, t2;

int n, K, Q, T, F[N], ot;

vector<int> w;


// void dfs3(int u) {
// 	vector<int> c;
// 	for (int v: t2.e[u]) {
// 		if (v == F[u]) continue;
// 		F[v] = u;
// 		dfs3(v);
// 		c.pb(v);
// 	}
// 	if (c.size()) t.pb(mp(w[0], c[0]));
// 	for (int i = 1; i < c.size(); i++) t.pb(mp(c[i], c[i - 1]));
	
// }

int main() {

    read(n), read(K), read(Q), read(T);
    for (int i = 1; i <= n; i++) {
    	int f; read(f);
    	if (f != -1) t1.g[f].pb(i), t1.g[i].pb(f);
    	else t1.rt = i;
    }
    for (int i = 1; i <= n; i++) {
    	int f; read(f);
    	if (f != -1) t2.g[f].pb(i), t2.g[i].pb(f);
    	else t2.rt = i;
    }
    t1.init(), t2.init();

    for (int i = 1; i <= K; i++) {
    	int u = t1.pre[i];
    	printf("%d", u);
    	if (i != K) putchar(' ');
    	w.pb(u);
    }
    puts(""); fflush(stdout);

    ot = t2.build(w);

    //dfs3(ot);


   // assert(t.size() <= Q);
    for (PII o: t) printf("? %d %d\n", o.fi, o.se);
    puts("!"); fflush(stdout);

	
	for (PII o: t) {
		int x = o.fi, y = o.se;
		int A, B, C, D; read(A), read(B), read(C), read(D);
		int l1 = t1.lca(x, y), l2 = t2.lca(x, y);
		t1.add(l1, x, A);
		t1.add(l1, y, B);
		t2.add(l2, x, C);
		t2.add(l2, y, D);
	}

	t1.bd(w[0]), t2.bd(w[0]);
	vector<PII> ans;
	for (int i = 1; i <= T; i++) {
		int x, y; read(x), read(y);
		ans.pb(mp(t1.D(x, y), t2.D(x, y)));
	}
	for (PII o: ans) printf("%d %d\n", o.fi, o.se);
	fflush(stdout);
    return 0;
}
posted @ 2022-07-26 20:43  DMoRanSky  阅读(517)  评论(0编辑  收藏  举报