T1 星際聯邦
菠蘿演算法。不過簡化版。考慮從後往前遍歷,如果當前的電 的聯通塊大小為 \(1\) 的話,就把他和字首最大值或者是字首最小值連邊。如果大於 \(1\),那就將聯通塊裡的最小權點和字首最大值連邊即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int N = 3e5 + 5;
int n, a[N], f[N], mn[N], Mx[N], Mn[N], siz[N];
inline int find(int k){ return f[k] ? f[k] = find(f[k]) : k; }
inline void merge(int a, int b){
a = find(a), b = find(b); f[b] = a;
mn[a] = min(mn[a], mn[b]);
siz[a] += siz[b];
}
signed main(){
freopen("star.in", "r", stdin); freopen("star.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n; for(int i=1; i<=n; ++i) cin>>a[i];
Mx[1] = 1; Mn[n] = n;
for(int i=2; i<=n; ++i){
Mx[i] = Mx[i-1];
if(a[Mx[i]] < a[i]) Mx[i] = i;
}
for(int i=n-1; i>=1; --i){
Mn[i] = Mn[i+1];
if(a[i] < a[Mn[i]]) Mn[i] = i;
}
for(int i=1; i<=n; ++i) mn[i] = a[i], siz[i] = 1;
int ans = 0;
for(int i=n; i>=2; --i){
if(siz[find(i)] == 1 && i != n){
if(a[i] - a[Mx[i-1]] < a[Mn[i+1]] - a[i]){
ans += a[i] - a[Mx[i-1]];
merge(i, Mx[i-1]);
} else {
ans += a[Mn[i+1]] - a[i];
merge(i, Mn[i+1]);
}
} else {
ans += mn[find(i)] - a[Mx[i-1]];
merge(i, Mx[i-1]);
}
} return cout<<ans, 0;
}
T2 和平精英
有一個顯然的性質就是,把一堆數與起來,大小是不升的,把一堆數或起來,大小是不降的。那麼假定一個值 \(val\) 顯然要把所有小於 \(val\) 的值或起來,把所有大於 \(val\) 的值與起來,等於 \(val\) 的值隨便放。
考慮如何最佳化,繼續發掘性質,把一堆數與起來,數中 \(1\) 的數量不升,把一堆數或起來,\(1\) 的數量不降。那麼假定一個 \(1\) 的數量 \(p\),顯然要把所有 \(1\) 的數量小於 \(p\) 的值或起來,把所有 \(1\) 的數量大於 \(p\) 的值與起來。等於 \(p\) 的數一定相等,若不相等 \(p\) 無效。單次查詢複雜度為 \(\mathcal{O}(n\log n)\)。使用線段樹維護複雜度為 \(\mathcal{O}((n+q)\log n)\)。
#include<bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 5;
int n, a[N], q;
namespace ST{
#define ls (id << 1)
#define rs (id << 1 | 1)
struct node{
int l, r, _or[31], _and[31], val[31], num[31];
bitset<31> orvis, andvis;
node(){
memset(_or, 0, sizeof(_or));
memset(val, 0, sizeof(val));
memset(num, 0, sizeof(num));
orvis.reset(), andvis.reset();
for(int i=0; i<=30; ++i) _and[i] = (1 << 30) - 1;
}
}t[N<<2];
inline void pushup(node &id, node _ls, node _rs){
for(int i=0; i<=30; ++i){
id._or[i] = _ls._or[i] | _rs._or[i];
id.val[i] = _ls.val[i] | _rs.val[i];
id._and[i] = _ls._and[i] & _rs._and[i];
id.andvis[i] = _ls.andvis[i] | _rs.andvis[i];
id.orvis[i] = _ls.orvis[i] | _rs.orvis[i];
id.num[i] = _ls.num[i] + _rs.num[i];
}
}
inline void build(int id, int l, int r){
t[id].l = l, t[id].r = r;
if(l == r){
for(int i=0; i<=30; ++i) t[id]._and[i] = (1 << 30) - 1;
int cnt = __builtin_popcount(a[l]);
t[id].val[cnt] = a[l]; t[id].num[cnt] = 1;
for(int i=0; i<cnt; ++i) t[id]._and[i] = a[l], t[id].andvis[i] = 1;
for(int i=cnt+1; i<=30; ++i) t[id]._or[i] = a[l], t[id].orvis[i] = 1;
return;
} int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid+1, r);
pushup(t[id], t[ls], t[rs]);
}
inline node query(int id, int l, int r){
if(l <= t[id].l && t[id].r <= r) return t[id];
int mid = (t[id].l + t[id].r) >> 1; node res; bool ok = 0;
if(l <= mid) res = query(ls, l, r), ok = 1;
if(r > mid){
if(!ok) res = query(rs, l, r);
else pushup(res, res, query(rs, l, r));
} return res;
}
}
int main(){
freopen("peace.in", "r", stdin); freopen("peace.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>q; for(int i=1; i<=n; ++i) cin>>a[i];
ST::build(1, 1, n);
auto check = [](int _and, int _or, int val, int num, bool vand, bool vor){
if(!num) return (_and == _or) && vand && vor;
if(num == 1) return ((_or | val) == _and && vand) || ((val & _and) == _or && vor);
return (_or | val) == (val & _and);
};
while(q--){
int l, r; cin>>l>>r;
ST::node res = ST::query(1, l, r);
bool ok = 0;
for(int i=0; i<=30; ++i) if((res.num[i] && __builtin_popcount(res.val[i]) == i) && check(res._and[i], res._or[i], res.val[i], res.num[i], res.andvis[i], res.orvis[i]))
{ ok = 1; break; }
cout<<(ok ? "YES" : "NO")<<'\n';
} return 0;
}
T3 擺爛合唱
給我一天也想不到,這東西可以建樹求解。樹的葉子節點為 \(0/1\),其他的為運算子。所以整個表示式的值就是根節點的值。令 \(g_u\) 表示當前節點的值為 \(1\) 的機率,根據與、或和異或的定義,有:
特殊地,葉子節點的 \(g\) 為 \(\frac{1}{2}\)。令 \(f_{u,0/1}\) 表示當左兒子或者右兒子取 \(0,1\) 的時候表示式值變化的機率,那麼有:
做兩次 dfs 即可求出。複雜度 \(\mathcal{O}(n)\)。注意特判 \(n=1\)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
constexpr int N = 2e5 + 5, M = 998244353;
int n, cnt, opt[N], rt, st[N], tl, g[N], ls[N], rs[N], f[N][2], ans[N];
string str;
inline int add(initializer_list<int> Add){
int res = 0;
for(int v : Add) res = res + v >= M ? res + v - M : res + v;
return res;
}
inline int mul(initializer_list<int> Mul){
int res = 1;
for(int v : Mul) res = (ll)res * v % M;
return res;
}
inline int mod(int x){ return (x + M) % M; }
inline int calc(int x){ return add({1, mod(-x)}); }
inline void dfs1(int u){
if(!ls[u]) return;
int l = ls[u], r = rs[u];
dfs1(l), dfs1(r);
if(opt[u] == 1){
g[u] = calc(mul({calc(g[l]), calc(g[r])}));
f[u][0] = calc(g[l]), f[u][1] = calc(g[r]);
} else if(opt[u] == 2){
g[u] = mul({g[l], g[r]});
f[u][0] = g[l], f[u][1] = g[r];
} else {
g[u] = add({mul({g[l], calc(g[r])}), mul({g[r], calc(g[l])})});
f[u][0] = f[u][1] = 1;
}
}
inline void dfs2(int u, int val){
if(!ls[u]) return ans[u] = val, void();
dfs2(ls[u], mul({val, f[u][1]}));
dfs2(rs[u], mul({val, f[u][0]}));
}
int main(){
freopen("binary.in", "r", stdin); freopen("binary.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>str; int len = str.size();
if(n == 1) return cout<<'1', 0;
for(int i=0; i<len; ++i){
if(str[i] == '|' || str[i] == '^' || str[i] == '&'){
opt[++cnt] = str[i] == '|' ? 1 : str[i] == '&' ? 2 : 3;
ls[cnt] = st[tl]; st[tl] = cnt;
} else if(str[i] == 'x'){
if(st[tl] == -1) st[++tl] = ++cnt, g[cnt] = 499122177;
else st[++tl] = ++cnt, g[cnt] = 499122177;
} else if(str[i] == ')'){
int x = st[tl--];
rs[rt = st[tl]] = x;
st[--tl] = rt;
} else st[++tl] = -1;
} dfs1(rt); dfs2(rt, 1);
for(int i=1; i<=cnt; ++i) if(!ls[i]) cout<<ans[i]<<'\n';
return 0;
}
T4 對稱旅行者
看樣子是個沒見過的 trick。如果說求所有情況的值的和,那麼可以考慮轉成期望來計算。令 \(f_i\) 表示旅行者 \(i\) 的期望位置,那麼有:\(f_i=\frac{1}{2}(2f_{i-1}-f_i+2f_{i+1}-f_i)=f_{i-1}+f_{i+1}-f_i\),也就是把 \(f_i\) 關於 \(f_{i-1}\) 和 \(f_{i+1}\) 的中點對稱。令 \(g_i=f_{i+1}-f_i\),那麼條第 \(i\) 枚棋子相當於交換 \(g_{i-1}\) 和 \(g_i\)。然後求置換即可。
#include<bits/stdc++.h>
using namespace std;
#define ll __int128
#define int long long
constexpr int N = 1e5 + 5, M = 1e9 + 7;
int m, n, to[N]; int k, g[N], f[N];
struct node{ int to[N]; } bg, ans;
inline int qpow(int a, int k){
int res = 1; while(k){
if(k & 1) res = (ll)res * a % M;
a = (ll)a * a % M; k >>= 1;
} return res;
}
inline int mul(int a, int b){ return ((ll)a * b % M + M) % M; }
signed main(){
freopen("travel.in", "r", stdin); freopen("travel.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n; for(int i=1; i<=n; ++i) cin>>f[i];
for(int i=1; i<n; ++i) g[i] = f[i+1] - f[i], to[i] = i;
cin>>m>>k; for(int i=1, a; i<=m; ++i) cin>>a, swap(to[a], to[a-1]);
for(int i=1; i<n; ++i) bg.to[to[i]] = i;
int p = qpow(qpow(2, k), m); --k;
auto merge = [](node a, node b){
node c;
for(int i=1; i<n; ++i) c.to[i] = b.to[a.to[i]];
return c;
}; ans = bg;
while(k){
if(k & 1) ans = merge(ans, bg);
bg = merge(bg, bg), k >>= 1;
}
for(int i=1; i<n; ++i) to[ans.to[i]] = i;
for(int i=2; i<=n; ++i) f[i] = f[i-1] + g[to[i-1]];
for(int i=1; i<=n; ++i) cout<<mul(f[i], p)<<' ';
return 0;
}