RMI 2021【杂题】

传送门:Day 1Day 2

present

将所有满足 \(x,y\in A\implies\gcd(x,y)\in A\)\(A\subset\mathbb N_+\)\(\sum_{a\in A}\omega^a\) 排序求第 \(k\) 小。

\(T\le 5\)\(k\le 1.5\cdot 10^9\)

solution

猜测 \(m:=\max A\) 不会很大,将 \([m]\) 划分为奇数和偶数两部分,奇数对偶数的限制为只能选某个子集内的数,于是 mitm,求答案按位贪心即可,时间复杂度 \(\mathcal O(Tm2^{m/2})\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1 << 19, biao[] = {0,2,4,7,13,22,38,67,121,208,346,663,1067,2084,3650,5621,10187,20228,33960,67673,106919,167302,316644,632549,988585,1672754,3243116,5502723,9032101,18060326,26876518,53747047,97409341,162001788,320354230,488138971,761529731,1523024388};
int T, k[5], _[38][38], mzk[N], sum[N];
vector<int> ans[5];
bitset<N> oke, okl;
int main(){
	ios::sync_with_stdio(false);
	cin >> T;
	for(int i = 0;i < T;++ i) cin >> k[i];
	for(int i = 1;i < 38;++ i)
		for(int j = 1;j < 38;++ j)
			_[i-1][j-1] = __gcd(i, j);
	for(int m = 1;m < 38;++ m){
		bool flg = false;
		for(int i = 0;i < T;++ i) flg |= k[i] >= biao[m-1] && k[i] < biao[m];
		if(!flg) continue;
		int md = m>>1, le = 1<<md, lo = 1<<m-md;
		memset(mzk, 0, lo<<2);
		oke.reset(); okl.reset();
		for(int S = 0;S < le;++ S){
			bool flg = true;
			for(int i = 0;i < md && flg;++ i) if(S >> i & 1)
				for(int j = i+1;j < md && flg;++ j)
					if((S >> j & 1) && !(S >> _[i][j]-1 & 1))
						flg = false;
			if(flg) oke.set(S);
		}
		for(int S = 0;S < lo;++ S){
			bool flg = true;
			for(int i = 0;i < m-md && flg;++ i) if(S >> i & 1)
				for(int j = i+1;j < m-md && flg;++ j)
					if((S >> j & 1) && !(S >> (_[i<<1][j<<1]>>1) & 1))
						flg = false;
			if(!flg) continue;
			okl.set(S);
			for(int j = 0;j < md;++ j){
				flg = true;
				for(int i = 0;i < m-md && flg;++ i)
					if((S >> i & 1) && !(S >> (_[i<<1][j]>>1) & 1))
						flg = false;
				if(flg) mzk[S] |= 1 << j;
			}
		}
		auto calc = [&](int od0, int od1, int ev0, int ev1){
			vector<int> tmp; tmp.clear();
			for(int i = 0;i < md;++ i)
				if(ev1 >> i & 1) tmp.push_back(i);
			int L = tmp.size(), lim = 1<<L;
			for(int i = 0;i < lim;++ i){
				int S = 0;
				for(int j = 0;j < L;++ j)
					if(i >> j & 1) S |= 1 << tmp[j];
				sum[i] = oke[ev0 | S];
			}
			for(int md = 1;md < lim;md <<= 1)
				for(int i = 0;i < lim;i += md<<1)
					for(int j = 0;j < md;++ j)
						sum[i | j | md] += sum[i | j];
			int res = 0;
			for(int i =	od1;;i = i-1 & od1){
				if(okl[od0 | i] && (mzk[od0 | i] & ev0) == ev0){
					int hah = 0;
					for(int j = 0;j < L;++ j)
						if(mzk[od0 | i] >> tmp[j] & 1) hah |= 1 << j;
					res += sum[hah];
				}
				if(!i) break;
			}
			return res;
		};
		for(int i = 0;i < T;++ i) if(k[i] >= biao[m-1] && k[i] < biao[m]){
			int od0 = 0, od1 = lo-1, ev0 = 0, ev1 = le-1;
			k[i] -= biao[m-1];
			(m & 1 ? od0 : ev0) |= 1 << (m-1>>1);
			(m & 1 ? od1 : ev1) &= ~(1 << (m-1>>1));
			ans[i].push_back(m);
			for(int j = m-1;j;-- j){
				(j & 1 ? od1 : ev1) &= ~(1 << (j-1>>1));
				int res = calc(od0, od1, ev0, ev1);
				if(k[i] >= res){
					k[i] -= res;
					(j & 1 ? od0 : ev0) |= 1 << (j-1>>1);
					ans[i].push_back(j);
				}
			}
			reverse(ans[i].begin(), ans[i].end());
			k[i] = 0;
		}
	}
	for(int i = 0;i < T;++ i){
		cout << ans[i].size();
		for(int u : ans[i]) cout << ' ' << u;
		cout << '\n';
	}
}

WeirdTree

给定长为 \(n\) 的整数序列 \(a_1,\cdots,a_n\)\(q\) 次询问形如:

  • 给定 \(l,r,k\)\(k\) 次”将最靠左的最大值减 \(1\)“;
  • 给定 \(i,x\),令 \(a_i:=x\)
  • 给定 \(l,r\),求 \(\sum_{i=l}^ra_i\)

\(n,q\le 3\cdot 10^5\)\(x,k,a_i\le 10^9\),强制在线。

solution

Segbeats 板子。

Paths

给定 \(n\) 个点的树和正整数 \(k\),边带非负权,设 \(\text{path}(u,v)\) 表示 \(u\)\(v\) 简单路径的边集,对所有 \(r\in [n]\)\(\bigcup_{i=1}^k\text{path}(r,v_i)\) 的权值和的最大值,其中 \(v_1,\cdots,v_k\) 是任意树上的点。

\(k,n\le 10^5\)\(w_i\le 10^9\)

solution

经典结论是贪心选 \(k\) 次使得答案增加最多的路径,暴力即得 \(\mathcal O(n^2\log n)\) 的做法。

需要仔细观察,设 \(\text{val}(x)\) 表示对于叶子 \(x\),选择它时答案的增量;以 \(1\) 为根,设 \(d_x,u_x\) 分别表示 \(x\) 子树内/外距离 \(x\) 最远的叶子,则当 \(r=1\)\(\text{val}(x)\) 即为最深的祖先 \(v\) 使得 \(d_v\ne x\)(若没有则 \(v=r\))的 \(\text{dis}(x,v)\)

再观察一下,根从 \(r\) 移向儿子 \(q\) 时只有 \(d_q\)\(u_q\)\(\text{val}\) 值会改变,所以支持单点修改、求全局前 \(k\) 大和即可,时间复杂度 \(\mathcal O(n\log n)\)

#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<LL, int> pii;
const int N = 300003;
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, k, cnt, hd[N], to[N], nxt[N], w[N], fa[N];
void add(int a, int b, int c){to[++cnt] = b; nxt[cnt] = hd[a]; hd[a] = cnt; w[cnt] = c;}
pii down[N], down2[N], up[N];
LL val[N], ans[N];
pii operator + (const pii &a, int b){return MP(a.fi + b, a.se);}
void dfs1(int x){
	down[x] = MP(0, x);
	for(int i = hd[x];i;i = nxt[i]) if(to[i] != fa[x]){
		fa[to[i]] = x; dfs1(to[i]);
		pii tmp = down[to[i]] + w[i];
		if(down[x] <= tmp){down2[x] = down[x]; down[x] = tmp;}
		else chmax(down2[x], tmp);
	}
	for(int i = hd[x];i;i = nxt[i])
		if(to[i] != fa[x] && down[x].se != down[to[i]].se)
			val[down[to[i]].se] = down[to[i]].fi + w[i];
}
void dfs2(int x){
	for(int i = hd[x];i;i = nxt[i]) if(to[i] != fa[x]){
		up[to[i]] = max(up[x], down[x] == down[to[i]] + w[i] ? down2[x] : down[x]) + w[i];
		dfs2(to[i]);
	}
}
LL hah[N], tot;
void dfs3(int x){
	for(int i = hd[x];i;i = nxt[i]) if(to[i] != fa[x]){
		hah[tot++] = val[down[to[i]].se] -= w[i];
		hah[tot++] = val[up[to[i]].se] += w[i];
		dfs3(to[i]);
		val[down[to[i]].se] += w[i];
		val[up[to[i]].se] -= w[i];
	}
}
LL trs[N];
int trc[N];
void upd(LL s, int c){
	int p = tot - (lower_bound(hah, hah + tot, s) - hah); s *= c;
	while(p <= tot){trs[p] += s; trc[p] += c; p += p & -p;}
}
LL qry(){
	int p = 0, now = k; LL sum = 0;
	for(int i = 18;~i;-- i)
		if((p | 1<<i) < tot && now >= trc[p | (1<<i)]){
			now -= trc[p |= (1<<i)]; sum += trs[p];
		}
	return sum + hah[tot - p - 1] * now;
}
void dfs4(int x){
	ans[x] = qry();
	for(int i = hd[x];i;i = nxt[i]) if(to[i] != fa[x]){
		upd(val[down[to[i]].se], -1);
		upd(val[down[to[i]].se] -= w[i], 1);
		upd(val[up[to[i]].se], -1);
		upd(val[up[to[i]].se] += w[i], 1);
		dfs4(to[i]);
		upd(val[down[to[i]].se], -1);
		upd(val[down[to[i]].se] += w[i], 1);
		upd(val[up[to[i]].se], -1);
		upd(val[up[to[i]].se] -= w[i], 1);
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> k;
	for(int i = 1, a, b, c;i < n;++ i){
		cin >> a >> b >> c;
		add(a, b, c); add(b, a, c);
	}
	dfs1(1); val[down[1].se] = down[1].fi; dfs2(1);
	for(int i = 1;i <= n;++ i) if(val[i]) hah[tot++] = val[i];
	dfs3(1);
	sort(hah, hah + tot);
	tot = unique(hah, hah + tot) - hah;
	for(int i = 1;i <= n;++ i) if(val[i]) upd(val[i], 1);
	dfs4(1);
	for(int i = 1;i <= n;++ i) printf("%lld\n", ans[i]);
}

Gardening

给定正整数 \(n,m,k\),给 \(n\times m\) 的网格图染 \(k\) 种颜色使得每种颜色都出现过且导出子图是简单环。需判断无解。

\(\sum nm\le 2\cdot 10^5\)

solution

形如一堆矩形套起来,分类讨论即可。不妨设 \(n\le m\) 则有解的条件是 \(2\mid n,m\)\(m/2\le k\le nm/4\)\(k\ne nm/4-1\)\(n=m\implies k\ne n/2+1\)

Speedrun

这是一道单向通信+交互题

  • 给定 \(n\) 个点的树,对每个点 \(x\) 输出长为 \(20\)\(\texttt{01}\)\(a_x\)
  • 给定正整数 \(n\) 和当前位置 \(x\),你可以调用下述函数若干次,使得遍历每个点至少一次,且 \(\texttt{goTo}\) 返回 \(\texttt{false}\) 的次数不超过 \(2000\)
    • \(\texttt{bool goTo(int y)}\),当 \(x\)\(y\) 相邻时返回 \(\texttt{true}\) 并令 \(x:=y\),否则返回 \(\texttt{false}\)
    • \(\texttt{bool getHint(int y)}\),返回 \(a_{x,y}\)

\(n\le 1000\)

solution

记录父亲和 DFS 序下一个的编号。

posted @ 2022-03-24 21:01  mizu164  阅读(174)  评论(0编辑  收藏  举报