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'; } }
NoM
給定正整數 \(n,m\),求長為 \(2n\) 的排列 \(p\) 滿足 \(|p_{2i}-p_{2i-1}|\ne m\) 的數量 \(\bmod(10^9+7)\)。
\(m\le n\le 2000\)。
solution
直接容斥,列舉有 \(i\) 個限制不滿足,設 \(f_i\) 表示在 \((2n\bmod m)\) 個長為 \(\lceil 2n/m\rceil\) 的鏈和 \(m-(2n\bmod m)\) 個長為 \(\lfloor 2n/m\rfloor\) 的鏈中選 \(i\) 個匹配的方案數,則答案為 \(\displaystyle\sum_{i=0}^n(-1)^i\binom ni2^i(2n-2i)!f_i\)
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)\)
\(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 序下一個的編號。