首页 > 其他分享 >2025-1-1 / 2025-1-2 做题笔记

2025-1-1 / 2025-1-2 做题笔记

时间:2025-01-02 21:19:02浏览次数:1  
标签:return int res pos 笔记 2025 id color

2025-1-1 / 2025-1-2 做题笔记

持续更新中……

目录

CF1534H - Lost Nodes

首先设 \(ans_i\) 为 \(f=i\) 中的最坏情况下的最小询问数,第一问就是 \(\max ans_i\),接下来考虑求解 \(ans_i\) 也就是第二问。

不难发现第一步应该是确定在根为 \(f\) 下,\(a\) 和 \(b\) 在哪颗子树里面,问出子树后继续递归是什么子树,所以直接设初步 dp,\(dp_u\) 为在根为 \(f\) 下,确定点在 \(u\) 里中的哪颗子树里,或者在 \(u\) 上的代价。

对于一个叶子 \(u\),需要检查一遍有没有点,代价为 \(1\)。

对于普通节点 \(u\),对于一个儿子 \(v\),需要检查点是否在 \(v\) 子树内,这需要 \(1\) 的代价,但是不管在 \(v\) 里询问什么,都可以得出点是否在 \(v\) 子树内,所以直接去根据 \(v\) 的最优策略问,如果没有就直接退出,所以对于第一个子树代价即为 \(dp_v\),若在子树 \(v\) 里没有点,这是浪费了一个代价,并且要去问下一个子树,以此类推,那么转移即为:

\[dp_u=\max_{i=0}^{sz_u-1}dp_{v_i}+i \]

要最小化这个东西,即为按照从大到小排序 \(dp_{v_i}\)。

对于 \(ans_u\),最多需要找到两个子树,那么最坏的代价为 \(\max_{0\le i<j<sz_u}dp_i+dp_j+j-1\),最小可以做到 \(dp_{v_0}+\max_{i=1}^{sz_u-1}dp_{v_i}+i-1\),其中 \(v_i\) 是通过将 \(dp_{v_i}\) 从大到小排完序后的结果。

上述都是对于根固定的情况,换根 dp 即可得出所有的 \(ans_i\)。

对于第二问,直接根据转移的方式,直接模拟即可。

时间复杂度 \(O(n \log n)\)。

ケロシの代码
const int N = 1e5 + 5;
int n, dp[N], F[N];
int a[N], b[N], f[N], g[N], t[N], len;
int fi[N], ne[N << 1], to[N << 1], ecnt;
vector<int> e[N];
void add(int u, int v) {
	ne[++ ecnt] = fi[u];
	to[ecnt] = v;
	fi[u] = ecnt;
}
void dfs1(int u, int fa) {
	for(int i = fi[u]; i; i = ne[i]) {
		int v = to[i];
		if(v == fa) continue;
		dfs1(v, u);
	}
	len = 0;
	for(int i = fi[u]; i; i = ne[i]) {
		int v = to[i];
		if(v == fa) continue;
		a[++ len] = dp[v];
	}
	sort(a + 1, a + len + 1);
	reverse(a + 1, a + len + 1);
	dp[u] = 1;
	FOR(i, 1, len) chmax(dp[u], a[i] + i - 1);
}
void dfs2(int u, int fa, int f0) {
	len = 0;
	for(int i = fi[u]; i; i = ne[i]) {
		int v = to[i];
		if(v == fa) continue;
		a[++ len] = dp[v];
	}
	a[++ len] = f0;
	sort(a + 1, a + len + 1);
	reverse(a + 1, a + len + 1);
	F[u] = 1;
	FOR(i, 2, len) chmax(F[u], a[1] + a[i] + i - 2);
	FOR(i, 1, len) f[i] = max(f[i - 1], a[i] + i - 1);
	g[len + 1] = 0;
	ROF(i, len, 1) g[i] = max(g[i + 1], a[i] + i - 1);
	FOR(i, 1, len) b[a[i]] = i;
	for(int i = fi[u]; i; i = ne[i]) {
		int v = to[i];
		if(v == fa) continue;
		int pos = b[dp[v]];
		t[v] = max({1, f[pos - 1], g[pos + 1] - 1});
	}
	for(int i = fi[u]; i; i = ne[i]) {
		int v = to[i];
		if(v == fa) continue;
		dfs2(v, u, t[v]);
	}
}
void dfs0(int u, int fa) {
	for(int i = fi[u]; i; i = ne[i]) {
		int v = to[i];
		if(v == fa) continue;
		dfs0(v, u);
	}
	for(int i = fi[u]; i; i = ne[i]) {
		int v = to[i];
		if(v == fa) continue;
		e[u].push_back(v);
	}
	sort(e[u].begin(), e[u].end(), [&] (int x, int y) {
		return dp[x] > dp[y];
	});
}
int query(int u) {
	cout << "? " << u << endl << flush;
	int v; cin >> v;
	return v;
}
int dfs(int u) {
	if(e[u].empty()) return query(u);
	for(int v : e[u]) {
		int val = dfs(v);
		if(val != u) return val;
	}
	return u;
}
void solve() {
	cin >> n;
	if(n == 1) {
		cout << 0 << endl << flush;
		int x; cin >> x;
		cout << "! " << 1 << " " << 1 << endl << flush;
		return;
	}
	REP(_, n - 1) {
		int u, v;
		cin >> u >> v;
		add(u, v), add(v, u);
	}
	dfs1(1, 0);
	dfs2(1, 0, 0);
	int ans = 0;
	FOR(i, 1, n) chmax(ans, F[i]);
	cout << ans << endl << flush;
	int rt; cin >> rt;
	int A = rt, B = rt;
	dfs1(rt, 0);
	dfs0(rt, 0);
	for(int v : e[rt]) {
		int val = dfs(v);
		if(val != rt) {
			if(A == rt) {
				A = val;
			}
			else {
				B = val;
				break;
			}
		}
	}
	cout << "! " << A << " " << B << endl << flush;
}

CF1510B - Button Lock

首先考虑建出单向图,对于点 \(S\) 和 点 \(T\),若 \(S \subset T\),则连一条 \(S\) 到 \(T\) 的边。因为有重置操作,所以再搞一个根连向所有点。

不难发现接下来就变为了一个最小路径覆盖问题,需要用从根开始的链覆盖所有的点,一条链的代价为根到链底所需的代价。

对于链覆盖问题,考虑网路流,因为有不同的代价权,考虑建费用流,类似普通最小链覆盖,把原图拆成二分图,每个点有入点 \(in(u)\) 和出点 \(out(u)\)。

若原图上 \(u\) 到 \(v\) 有边,则在费用流上建 \(in(u) \to out(v)\),流量为 \(1\),费用为 \(-w(u)\),意义是通过走 \(u\) 到 \(v\),省去了点 \(u\) 的代价。

因为一个点不可能省或被省去两次,且原图本来就是一个闭包,所以直接再建出原点 \(S\) 和汇点 \(T\),连 \(S \to in(u)\) 和 \(out(u) \to T\),流量为 \(1\),费用为 \(0\)。

但是直接跑费用流可能跑不过,考虑到费用的范围很小,根据费用流每次增广最短路,所以直接从小到大枚举费用加边,每次跑一遍最大流即可。

时间复杂度 \(O(d2^d\sqrt{3^d})\)。

ケロシの代码
namespace Dinic {
	const int N = 3e3 + 5;
	const int M = 1e6 + 5;
	const int INF = 0x3f3f3f3f;
	struct Edge {
		int ne, to, ew;
	} e[M];
	int S, T; 
	int fi[N], c[N], ecnt;
	int d[N];
	void init() {
		memset(fi, 0, sizeof fi);
		ecnt = 1;
	}
	void add(int u, int v, int w) {
		e[++ ecnt] = {fi[u], v, w};
		fi[u] = ecnt;
		e[++ ecnt] = {fi[v], u, 0};
		fi[v] = ecnt;
	}
	bool bfs() {
		memset(d, 0x3f, sizeof d);
		queue<int> q;
		d[S] = 0; q.push(S);
		while(! q.empty()) {
			int u = q.front();
			q.pop();
			for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
				int v = e[i].to;
				if(d[v] == INF) {
					d[v] = d[u] + 1;
					q.push(v);
				}
			}
		}
		return d[T] != INF;
	}
	int dfs(int u, int w) {
		if(u == T || ! w) return w;
		int res = 0;
		for(int & i = c[u]; i; i = e[i].ne) {
			int v = e[i].to;
			if(d[v] != d[u] + 1) continue;
			int val = dfs(v, min(e[i].ew, w));
			e[i].ew -= val;
			e[i ^ 1].ew += val;
			w -= val;
			res += val;
			if(! w) return res;
		}
		return res;
	}
	int dinic(int _S, int _T) {
		S = _S, T = _T;
		int res = 0;
		while(bfs()) {
			memcpy(c, fi, sizeof c);
			res += dfs(S, INF);
		}
		return res;
	}
}
const int N = 1.1e3 + 5;
int d, n, a[N], vis[N];
vector<PII> e[N];
void add(int u, int v, int w) {
	e[w].push_back({u, v});
}
void print(int x, int y) {
	REP(i, d) if((x >> i & 1) != (y >> i & 1))
		cout << i << " ";
}
void dfs(int u) {
	vis[u] = 1;
	for(int i = Dinic :: fi[u]; i; i = Dinic :: e[i].ne) {
		int v = Dinic :: e[i].to - n, w = Dinic :: e[i].ew;
		if(1 <= v && v <= n && ! w) {
			print(a[u], a[v]);
			dfs(v);
		} 
	}
}
void solve() {
	cin >> d >> n;
	FOR(i, 1, n) {
		string s; cin >> s;
		ROF(j, d - 1, 0) a[i] = a[i] << 1 | (s[j] == '1');
	}
	n ++;
	sort(a + 1, a + n + 1);
	int S = 0, T = n * 2 + 1;
	Dinic :: init();
	FOR(i, 2, n) add(1, i + n, 1);
	FOR(i, 2, n) FOR(j, i + 1, n) if((a[i] & a[j]) == a[i])
		add(i, j + n, popcount(a[i]) + 1);
	FOR(i, 1, n) Dinic :: add(S, i, 1);
	FOR(i, 1, n) Dinic :: add(i + n, T, 1);
	int ans = - 1;
	FOR(i, 1, n) ans += popcount(a[i]) + 1;
	ROF(i, d + 1, 1) {
		for(auto h : e[i])
			Dinic :: add(FI(h), SE(h), 1);
		ans -= Dinic :: dinic(S, T) * i;
	}
	cout << ans << endl;
	FOR(i, 1, n) if(! vis[i]) {
		if(i > 1) cout << "R ";
		print(0, a[i]);
		dfs(i);
	}
}

CF1336E1 - Chiori and Doll Picking (easy version)

首先将所有 \(n\) 个数插入线性基,有经典结论,线性基 \(A\) 里能异或出来的数的方案数都是 \(2^{n-|A|}\),因为无论外边 \(n-|A|\) 个数怎么异或,线性基里总能得出一种异或方法异或到某个数。

接下来就只需在线性基里计算即可,但是线性基大小比较大,比较难计算。

考虑拆成高低位,不难发现低位是影响不到高位的,所以将高位异或出的数的高位部分独立出来,设 \(g_S\) 为低位中异或出来为 \(S\) 的方案数,设 \(f_{i,S}\) 为高位中异或出来的数,在高位的 popcount 为 \(i\),低位部分为 \(S\) 的方案数。

接下来对于每个高位 popcount 取值,都做使用 FWT 进行一遍异或卷积来计算低位异或部分:

\[h_{i,S}=\sum_{j\oplus k=S}f_{i,j}g_k \]

就能得出不同低位取值和高位 popcount 的方案数,然后就能直接统计出答案了。

时间复杂度 \(O(m^22^\frac{m}{2})\)。

ケロシの代码
const int N = 40;
const int P = 998244353;
const int P2 = (P + 1) / 2;
inline int add(int x, int y) { return (x + y < P ? x + y : x + y - P); }
inline void Add(int & x, int y) { x = (x + y < P ? x + y : x + y - P); }
inline int sub(int x, int y) { return (x < y ? x - y + P : x - y); }
inline void Sub(int & x, int y) { x = (x < y ? x - y + P : x - y); }
inline int mul(int x, int y) { return (1ll * x * y) % P; }
inline void Mul(int & x, int y) { x = (1ll * x * y) % P; }
int fp(int x, int y) {
	int res = 1;
	for(; y; y >>= 1) {
		if(y & 1) Mul(res, x);
		Mul(x, x);
	}
	return res;
}
int n, m;
ll b[N];
int p[N], len, a[20][1 << 18];
int f[1 << 18], g[1 << 18];
int ans[N];
void insert(ll x) {
	ROF(i, m - 1, 0) if(x >> i & 1) {
		if(! b[i]) {
			b[i] = x;
			return;
		}
		x ^= b[i];
	}
}
void XOR(int * a, int lim) {
	for(int i = 1; i < lim; i <<= 1) 
		for(int j = 0; j < lim; j += (i << 1))
			REP(k, i) {
				int x = a[j + k];
				int y = a[j + k + i];
				a[j + k] = add(x, y);
				a[j + k + i] = sub(x, y);
			}
}
void IXOR(int * a, int lim) {
	for(int i = 1; i < lim; i <<= 1) 
		for(int j = 0; j < lim; j += (i << 1))
			REP(k, i) {
				int x = mul(a[j + k], P2);
				int y = mul(a[j + k + i], P2);
				a[j + k] = add(x, y);
				a[j + k + i] = sub(x, y);
			}
}
void solve() {
	cin >> n >> m;
	REP(_, n) {
		ll x; cin >> x;
		insert(x);
	}
	int mid = m / 2;
	FOR(i, mid, m - 1) if(b[i]) p[len ++] = i;
	REP(S, 1 << len) {
		ll val = 0;
		REP(i, len) if(S >> i & 1) val ^= b[p[i]];
		a[popcount(val >> mid)][val & ((1 << mid) - 1)] ++;
	}
	len = 0;
	REP(i, mid) if(b[i]) p[len ++] = i;
	REP(S, 1 << len) {
		ll val = 0;
		REP(i, len) if(S >> i & 1) val ^= b[p[i]];
		g[val] ++;
	}
	XOR(g, 1 << mid);
	FOR(i, 0, m - mid) {
		REP(S, 1 << mid) f[S] = a[i][S];
		XOR(f, 1 << mid);
		REP(S, 1 << mid) Mul(f[S], g[S]);
		IXOR(f, 1 << mid);
		REP(S, 1 << mid) Add(ans[i + popcount(S)], f[S]);
	}
	int sz = 0;
	REP(i, m) if(b[i]) sz ++;
	FOR(i, 0, m) Mul(ans[i], fp(2, n - sz));
	FOR(i, 0, m) cout << ans[i] << " "; cout << endl;
}

UOJ R28B - 环环相扣

不妨设 \(a_i>a_j>a_k\),那么最优的排法只有两种情况:\((a_i \bmod a_j) + (a_j \bmod a_k) + a_k\) 和 \((a_i \bmod a_k) + a_j + a_k\)。

考虑重要性质:若 \(x>y\),那么 \((x \bmod y)+y \le (x - y)+y = x\),也就是 \((x \bmod y)+ y\le x\)。

接下来通过手玩,发现对于区间的最优解中,区间的最大值和次大值是必选的。

证明 ………………

接下来考虑继续优化,…………
…………

…………

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

ケロシの代码
namespace IO {
	char buf[1024], * p1 = buf, * p2 = buf;
	inline char gc() {
		if(p1 == p2) {
			p2 = buf + fread(buf, 1, sizeof buf, stdin);
			p1 = buf;
		}
		return p1 == p2 ? EOF : * p1 ++;
	}
	ll read() {
		ll res = 0; int v = 0;
		char c = gc();
		while(c < '0' || c > '9') {
			if(c == '-') v = 1;
			c = gc();
		}
		while('0' <= c && c <= '9') {
			res = (res << 3) + (res << 1) + (c ^ 48);
			c = gc();
		}
		return v ? - res : res;
	}
}
using IO :: read;
const int N = 2e6 + 5;
const ll LNF = 1e18;
int n, m, op;
ll a[N];
vector<pair<int, ll>> f[N], g[N];
struct SgT {
	int le[N << 2], ri[N << 2];
	int D[N << 2];
	void pushup(int u) {
		D[u] = a[D[u << 1]] >= a[D[u << 1 | 1]] ? D[u << 1] : D[u << 1 | 1];
	}
	void build(int u, int l, int r) {
		le[u] = l, ri[u] = r;
		if(l == r) {
			D[u] = l;
			return;
		}
		int mid = l + r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
	int query(int u, int l, int r) {
		if(l > r) return 0;
		if(l <= le[u] && ri[u] <= r) {
			return D[u];
		}
		int mid = le[u] + ri[u] >> 1;
		if(r <= mid) return query(u << 1, l, r);
		if(mid < l) return query(u << 1 | 1, l, r);
		int L = query(u << 1, l, r);
		int R = query(u << 1 | 1, l, r);
		return a[L] >= a[R] ? L : R;
	}
} t;
ll F(int l, int r, int i) {
	ll res = - LNF;
	int L = 0, R = SZ(g[i]) - 1;
	while(L <= R) {
		int mid = L + R >> 1;
		if(FI(g[i][mid]) <= r) {
			chmax(res, SE(g[i][mid]));
			L = mid + 1;
		}
		else {
			R = mid - 1;
		}
	}
	L = 0, R = SZ(f[i]) - 1;
	while(L <= R) {
		int mid = L + R >> 1;
		if(FI(f[i][mid]) >= l) {
			chmax(res, SE(f[i][mid]));
			L = mid + 1;
		}
		else {
			R = mid - 1;
		}
	}
	return res;
}
void solve() {
	n = read(); m = read(); op = read();
	FOR(i, 1, n) a[i] = read();
	FOR(l, 1, n) {
		ll res = - LNF; int pos = 0, cnt = 0;
		FOR(i, l + 1, n) {
			ll x = 0;
			if(a[i] > a[pos]) x = a[pos], pos = i;
			else x = a[i];
			if(x && chmax(res, a[l] % x + x))
				g[l].push_back({i, res});
			if(a[i] > a[l] / 2) cnt ++;
			if(cnt == 2) break;
		}
		res = - LNF; pos = cnt = 0;
		ROF(i, l - 1, 1) {
			ll x = 0;
			if(a[i] > a[pos]) x = a[pos], pos = i;
			else x = a[i];
			if(x && chmax(res, a[l] % x + x))
				f[l].push_back({i, res});
			if(a[i] > a[l] / 2) cnt ++;
			if(cnt == 2) break;
		}
	}
	t.build(1, 1, n);
	ll lst = 0;
	REP(_, m) {
		int l, r;
		l = read(); r = read();
		if(op) {
			l = (lst + l) % n; if(! l) l = n;
			r = (lst + r) % n; if(! r) r = n;
		}
		int p1 = t.query(1, l, r);
		int pl = t.query(1, l, p1 - 1);
		int pr = t.query(1, p1 + 1, r);
		int p2 = a[pl] > a[pr] ? pl : pr;
		ll res = - LNF;
		chmax(res, F(l, r, p1) + a[p2]);
		chmax(res, F(l, r, p2) + a[p1] % a[p2]);
		if(p1 < p2) {
			if(l < p1) {
				int p3 = t.query(1, l, p1 - 1);
				chmax(res, a[p1] % a[p3] + a[p3] + a[p2]);
			}
			if(p2 < r) {
				int p3 = t.query(1, p2 + 1, r);
				chmax(res, a[p2] % a[p3] + a[p3] + a[p1] % a[p2]);
			}
		}
		if(p2 < p1) {
			if(p1 < r) {
				int p3 = t.query(1, p1 + 1, r);
				chmax(res, a[p1] % a[p3] + a[p3] + a[p2]);
			}
			if(l < p2) {
				int p3 = t.query(1, l, p2 - 1);
				chmax(res, a[p2] % a[p3] + a[p3] + a[p1] % a[p2]);
			}
		}
		cout << (lst = res) << endl;
	}
}

AT NOMURA2020F - Sorting Game

先考虑固定的 \(a\) 数组,对于 \(i<j\) 且 \(a_i>a_j\) 的对,\(i\) 和 \(j\) 必须相邻交换一次,所以对于所有的 \(i<j\) 且 \(a_i>a_j\),必须满足 \(a_i\) 与 \(a_j\) 数位上只差一位。

更深入的,这就相当于 \(a_i\) 和 \(a_j\) 的第一个不同高位,\(a_i\) 的是 \(1\) 且 \(a_j\) 是 \(0\),且下面的位全部相同。

再考虑如果要修改这个 \(a\) 数组,一次修改只会推平一个位,两个数的不同位数并不会增大,只会操作最高不同位,使得 \(a_i<a_j\) 变为 \(a_i>a_j\)。

不难发现上述的交换和改数组都是高位到低位,所以考虑 dp。

社 \(f_{n,m}\) 为数组长为 \(m\),值域为 \([0,2^n)\) 的答案,那么考虑最高位是什么情况。

第一种是不存在 \(10\) 逆序对,形如 \(00\cdots 0011\cdots 11\),这时因为有左边 \(0\) 部分小于右边 \(1\) 部分,所以最佳改数组方案就是推平这一位,这样这一位的偏序就没有用了,相当于去掉这一位,递归进子问题。\(01\) 分割点有 \(m+1\) 种选法,方案即为 \((m+1)f_{n-1,m}\)。

第二种是存在 \(10\) 逆序对,考虑提出最左的 \(1\) 和最右的 \(0\),形如 \(0001\cdots 0111\),那么根据上述的性质,中间的 \(1\cdots 0\) 段下面的位都是相同的,直接随最左的 \(1\) 固定掉。接下来仍然可以推平这一位,使得最左的 \(0\) 段和最右的 \(1\) 段失去偏序关系,所以继续递归进子问题,枚举保留的有 \(i\) 位,那么方案为 \(\sum_{i=1}^{m-1}i \cdot 2^{m-i+1}f_{n-1,i}\)。

用前缀和优化这个转移即可,时间复杂度 \(O(n^2)\)。

ケロシの代码
const int N = 5e3 + 5;
const int P = 1e9 + 7;
inline int add(int x, int y) { return (x + y < P ? x + y : x + y - P); }
inline void Add(int & x, int y) { x = (x + y < P ? x + y : x + y - P); }
inline int mul(int x, int y) { return (1ll * x * y) % P; }
inline void Mul(int & x, int y) { x = (1ll * x * y) % P; }
int n, m;
int f[N][N], g[N][N];
void solve() {
	cin >> n >> m;
	FOR(j, 1, m) f[0][j] = 1;
	FOR(j, 1, m) g[0][j] = add(mul(g[0][j - 1], 2), mul(j, f[0][j]));
	FOR(i, 1, n) FOR(j, 1, m) {
		f[i][j] = mul(f[i - 1][j], j + 1);
		Add(f[i][j], g[i - 1][j - 1]);
		g[i][j] = add(mul(g[i][j - 1], 2), mul(j, f[i][j]));
	}
	cout << f[n][m] << endl;
}

AT JOISC2017E - 壊れた機器 (Broken Device)

考虑坏掉的位是零,所以需要用含有 \(1\) 的信息来表达进制中的 \(0\)。

这样的话一个位就表达不了信息,考虑用两个位存一个进制。

两个位可以存三种状态:\(01,10,11\),发现这可以表示三进制,分别对应 \(0,1,2\)。

但是最坏情况下这样不坏掉的两个位最少只有 \(\frac{150-40\times 2}{2}=35\) 个,只能表达 \(3^{35}\) 级别的数,而 \(10^{18}\) 需要 \(38\) 个位来表示。

考虑将位随机乱排,这样坏掉的位有几率排到一起,有效位就能增加,但不足以通过。

考虑有两个位,前一个位坏了但后一个是好的,那么这个两个位依然可以表示 \(01\),将这类情况下的位也充分利用,即可通过 \(N=150\)。

ケロシの代码
#include "Broken_device_lib.h"
const int N = 205;
int t[N], b[N], id[N];
void Anna(int n, ll x, int k, int * p) {
	mt19937 rnd(114);
	REP(i, n) id[i] = i;
	shuffle(id, id + n, rnd);
	REP(i, n) t[i] = b[i] = 0;
	REP(i, k) t[p[i]] = 1;
	int pos = 0;
	while(x) {
		int val = x % 3;
		if(val == 0) {
			while(t[id[pos]]) pos += 2;
			b[id[pos]] = 1;
		}
		if(val == 1) {
			while(t[id[pos + 1]]) pos += 2;
			b[id[pos + 1]] = 1;
		}
		if(val == 2) {
			while(t[id[pos]] || t[id[pos + 1]]) pos += 2;
			b[id[pos]] = b[id[pos + 1]] = 1;
		}
		pos += 2;
		x /= 3;
	}
	REP(i, n) Set(i, b[i]);
}
ll Bruno(int n, int * a) {
	mt19937 rnd(114);
	REP(i, n) id[i] = i;
	shuffle(id, id + n, rnd);
	ll x = 0;
	for(int pos = n - 2; pos >= 0; pos -= 2) {
		if(! a[id[pos]] && ! a[id[pos + 1]]) continue;
		x *= 3;
		if(a[id[pos]] && a[id[pos + 1]]) x += 2;
		if(! a[id[pos]] && a[id[pos + 1]]) x += 1;
	}
	return x;
}

考虑足以通过 \(N=120\) 甚至更小的更优做法。

考虑随机 \(n\) 个数 \(a_i\),如果构造出来的序列中第 \(i\) 位是 \(1\) 就表示答案就异或上 \(a_i\)。

这样只需选定一些数异或出目标数,使用线性基维护即可。

ケロシの代码
#include "Broken_device_lib.h"
const int N = 1000;
ll a[N], b[N], w[N];
int t[N], id[N], ans[N]; 
void insert(ll x, int u) {
	ll res = 0;
	ROF(i, 60, 0) if(x >> i & 1) {
		if(! b[i]) {
			b[i] = x;
			id[i] = u;
			w[i] = res ^ (1ll << i);
			return;
		}
		x ^= b[i];
		res ^= w[i];
	}
}
void Anna(int n, ll x, int k, int * p) {
	mt19937 rnd(114);
	REP(i, n) a[i] = 1ull * rnd() * rnd() % (1ll << 60);
	REP(i, 61) w[i] = b[i] = 0;
	REP(i, n) ans[i] = t[i] = 0;
	REP(i, k) t[p[i]] = 1;
	REP(i, n) if(! t[i]) insert(a[i], i);
	ll res = 0;
	ROF(i, 60, 0) if(x >> i & 1) {
		x ^= b[i];
		res ^= w[i];
	}
	ROF(i, 60, 0) if(res >> i & 1) ans[id[i]] = 1; 
	REP(i, n) Set(i, ans[i]);
}
ll Bruno(int n, int * p) {
	mt19937 rnd(114);
	REP(i, n) a[i] = 1ull * rnd() * rnd() % (1ll << 60);
	ll res = 0;
	REP(i, n) if(p[i]) res ^= a[i];
	return res;
}

SYSUCPC 2024 Final M - Make SYSU Great Again 3

首先考虑是链怎么做到答案为 \(\left \lceil \frac{n}{2} \right \rceil -1\),考虑将排列分为奇数位和偶数位,对于偶数位,都填 \(\left [1,\left \lfloor \frac{n}{2} \right \rfloor \right ]\) 中的数,奇数位填其余的数。

接下来就不难想到一个构造,偶数位直接从 \(1\) 开始填,不断填到大的,然后偶数位在满足条件时类似折返一样填即可,例如:

\[\color{red} 7 ~ \color{blue} 1 ~ \color{red} 8 ~ \color{blue} 2 ~ \color{red} 6 ~ \color{blue} 3 ~ \color{red} 9 ~ \color{blue} 4 ~ \color{red} 5 \]

\[\color{red} 8 ~ \color{blue} 1 ~ \color{red} 9 ~ \color{blue} 2 ~ \color{red} 7 ~ \color{blue} 3 ~ \color{red} {10} ~ \color{blue} 4 ~ \color{red} 6 ~ \color{blue} 5 \]

然后考虑是环怎么做,不难发现可以交换前两项,这样不影响最左边三个数的匹配,同时开头的 \(1\) 给最后两个数也产生了贡献:

\[\color{blue} 1 ~ \color{red} 7 ~ \color{red} 8 ~ \color{blue} 2 ~ \color{red} 6 ~ \color{blue} 3 ~ \color{red} 9 ~ \color{blue} 4 ~ \color{red} 5 \]

\[\color{blue} 1 ~ \color{red} 8 ~ \color{red} 9 ~ \color{blue} 2 ~ \color{red} 7 ~ \color{blue} 3 ~ \color{red} {10} ~ \color{blue} 4 ~ \color{red} 6 ~ \color{blue} 5 \]

ケロシの代码
const int N = 2e5 + 5;
int n, a[N];
void solve() {
	cin >> n;
	FOR(i, 1, n) a[i] = 0;
	if(n % 2) {
		for(int i = 1, p = 2; p <= n; p += 2, i ++)
			a[p] = i;
		for(int i = n / 2 + 1, p = n; p >= 1; p -= 4, i ++)
			a[p] = i;
		for(int i = n, p = n - 2; p >= 1; p -= 4, i --)
			a[p] = i;
		swap(a[1], a[2]);
	}
	else {
		for(int i = 1, p = 2; p <= n; p += 2, i ++)
			a[p] = i;
		for(int i = n / 2 + 1, p = n - 1; p >= 1; p -= 4, i ++)
			a[p] = i;
		for(int i = n, p = n - 3; p >= 1; p -= 4, i --)
			a[p] = i;
		swap(a[1], a[2]);
	}
	cout << "Yes" << endl;
	FOR(i, 1, n) cout << a[i] << " "; cout << endl;
}

标签:return,int,res,pos,笔记,2025,id,color
From: https://www.cnblogs.com/kevinlikescoding/p/18648103

相关文章

  • 2025你好
    2024即将过去,回顾这一年应该是人生中的重大转折之年,从服务11年公司退役,从青年进入了尴尬的中年,周围和网上有很多声音抱怨“中年男人不如狗”,因为上有老下有小,所有问题都需要自己抗。好在几年前遇到了挫折看了一些哲学知识,接受一切发生的事务,与自己和解,感谢曾老先生!2024回顾一、......
  • 2025.1.2复习
    2025.1.2复习用户态(UserMode)执行的任务:运行用户程序应用程序(如浏览器、文本编辑器、游戏等)通常在用户态下运行。用户态程序没有直接访问硬件和系统资源的权限,它们只能通过系统调用来请求操作系统的服务。内存管理用户态进程使用的是虚拟内存。用户程序可以访问其虚拟......
  • Y combinator的2025预测:AI将拿下数学或经济学诺奖、实现AI视频对话、稳定币将愈发重要
    内容提要Ycombinator预计25年加密货币将成为主流,稳定币会成为日常交易的一部分;如果“政府效率部”计划成功,将间接推动狗狗币价格上涨;在AI视频对话中,AI将拥有自己的虚拟形象和面部表情,整个互动将会像和真人对话一样自然。文章正文2025年AI将横扫诺奖?每个人都会用稳定币来买......
  • [数据结构学习笔记2] 大O法表示程序的时间复杂度
    程序运行都是需要时间的,我们用大O法来表示,程序在最坏情况下,运行时间和输入规模的关系。一般有这么几种大O时间:快:闪电:O(1)-常量复杂度-和输入无关;比如通过数组下标访问元素;简单数学运算,如看末尾数字是否是基数;火箭:O(logn)-对数复杂度-时间增长随数字规模增长缓慢;这种......
  • [数据结构学习笔记1] 为什么需要有数据结构
    程序本质上就是用来读取数据,然后操作数据,最后生成数据的。如果数据能被有效,或者有结构的展现,那将极大方便程序操作。举例:我们家里有很多工具,剪刀,锤子,斧头,扳手,放大镜,六角扳手,螺丝刀,尺子,卷尺,螺丝,便利贴等等。我们可以怎样收纳这些工具,使得我们后续可以方便的使用呢?第一种,我们家有......
  • 以太坊 solidity 笔记
    基础知识gasgas是衡量执行某些操作所需的计算量的单位,用来计算为了执行操作而需要支付给网络的费用数额。通俗理解,Gas是给矿工的佣金,并以ETH支付,无论是交易、执行智能合约并启动DApps,还是支付数据存储费用,都需要用到Gas。Gas的目的是限制执行交易所需的工作量,同时为执行......
  • Java学习笔记06-多态polymorphism
    一、多态1、含义:多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态多态的具体代码体现:packageorg.example.polymorphism1;publicclassAnimal{publicStringname="动物";publicvoidrun(){System.out.println("动物跑");}......
  • 2025年第七批国家级专精特新“小巨人”企业申报的八大要点
    随着2025年的临近,第七批国家级专精特新“小巨人”企业的申报工作即将展开。这对于众多中小企业来说,不仅是一次展现企业实力的机会,也是获取政策支持、提升市场竞争力的重要途径。申报成功的企业将获得国家层面的认可和一系列优惠政策。以下是申报的八大要点,企业需重点关注以提高......
  • Java学习笔记05-继承extends-03
    一、构造器用this(...)调用兄弟构造器作用:在构造器中调用本类的其他构造器注意事项:super(...)和this(...)必须写在构造器的第一行,且二者不能同时出现。代码演示:1、首先定义一个学生类,写好get和set方法,构造器和重写toString()方法packageorg.example.extends6constructor;......
  • 线性基学习笔记
    线性基很好理解,可以理解成\(n\)维的向量。我们先考虑\(n=2\),这是我们最熟悉的,可以在平面直角坐标系上表示出来。众所周知,在一个平面内,两个不共线的向量\(e_1\)和\(e_2\),可作为基底,即所有可在原坐标系上表示的向量\(x\)均可被\(e_1\)和\(e_2\)表示为\(\lambdae_1+......