RMI 2021【杂题】
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 序下一个的编号。