2024 Autumn Training #2 CG (by hzy)
C. Black-White Cubic Lattice (网络流)
大意:三维空间 \(n*m*l\) 格点黑白染色,已有初始色,每个点有翻转的代价 \(w\),要求以最小的代价构造 \((1,1,1)\) 为黑,\((n,m,l)\) 为白,且不存在内白外黑的点对。
禁止内白外黑,考虑最小割,每个点向内连边 \(inf\),白点流出 \(w\),黑点流入\(w\),则最小割就是不存在外黑->内白的路径的最小代价。注意特判两个特殊点。
#include <bits/stdc++.h>
#define ll long long
#define N
#define MOD 998244353
using namespace std;
template <typename T>
struct Flow_
{
const int n;
const T inf = numeric_limits<T>::max();
struct Edge
{
int to;
T w;
Edge(int to, T w) : to(to), w(w) {}
};
vector<Edge> ver;
vector<vector<int>> h;
vector<int> cur, d;
Flow_(int n) : n(n + 1), h(n + 1) {}
void add(int u, int v, T c)
{
h[u].push_back(ver.size());
ver.emplace_back(v, c);
h[v].push_back(ver.size());
ver.emplace_back(u, 0);
}
bool bfs(int s, int t)
{
d.assign(n, -1);
d[s] = 0;
queue<int> q;
q.push(s);
while (!q.empty())
{
auto x = q.front();
q.pop();
for (auto it : h[x])
{
auto [y, w] = ver[it];
if (w && d[y] == -1)
{
d[y] = d[x] + 1;
if (y == t)
return true;
q.push(y);
}
}
}
return false;
}
T dfs(int u, int t, T f)
{
if (u == t)
return f;
auto r = f;
for (int &i = cur[u]; i < h[u].size(); i++)
{
auto j = h[u][i];
auto &[v, c] = ver[j];
auto &[u, rc] = ver[j ^ 1];
if (c && d[v] == d[u] + 1)
{
auto a = dfs(v, t, std::min(r, c));
c -= a;
rc += a;
r -= a;
if (!r)
return f;
}
}
return f - r;
}
T work(int s, int t)
{
T ans = 0;
while (bfs(s, t))
{
cur.assign(n, 0);
ans += dfs(s, t, inf);
}
return ans;
}
};
using Flow = Flow_<int>;
// 禁止内白外黑
void solve()
{
int n,m,l;
cin>>n>>m>>l;
vector<vector<vector<int>>> g(n+1, vector<vector<int>>(m+1, vector<int>(l+1)));
for(int k=0;k<l;++k)
{
for(int i=0;i<n;++i)
{
string s; cin>>s;
// cout<<s<<'\n';
for(int j=0;j<m;++j)
{
if(s[j] == 'B') g[i][j][k] = 0;
else g[i][j][k] = 1;
}
}
}
int s = 0, t = n*m*l+1;
Flow flow(n*m*l+2);
auto id = [&](int i,int j,int k){return i*m*l + j*l + k + 1;}; // [1, n*m*l]
int ans = 0;
for(int k=0;k<l;++k)
{
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
int w; cin>>w;
if(!i && !j && !k) // 必黑
{
if(g[i][j][k] == 1) ans += w;
flow.add(s, id(i,j,k), flow.inf);
}
else if(i==n-1 && j==m-1 && k==l-1) // 必白
{
if(g[i][j][k] == 0) ans += w;
flow.add(id(i,j,k), t, flow.inf);
}
else //s流入到黑,白流出到t
{
if(g[i][j][k] == 0) flow.add(s, id(i,j,k), w);
else flow.add(id(i,j,k), t, w);
}
// 由外向内流
if(i>0) flow.add(id(i,j,k), id(i-1,j,k), flow.inf);
if(j>0) flow.add(id(i,j,k), id(i,j-1,k), flow.inf);
if(k>0) flow.add(id(i,j,k), id(i,j,k-1), flow.inf);
}
}
}
ans += flow.work(s,t);
cout<<ans<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
G. Function Query (字典树)
大意:给定序列 \(x_i\) ,有\(q\) 个询问,每个询问给出 \(a,b\),设 \(f(x) = a \oplus x - b\) 求 \(f(x_i)\) 正负号变化的位置(0也算)。
异或容易想到给 \(x_i\) 从高到低建立01字典树,每次可以找到\(a \oplus x = b\) 的一条极大路径,如果路径能到达叶子结点,那么该叶子对应i就是答案;到不了叶子,则此路径上的“外拐分支”必定对应 \(<\) 子树或 \(>\) 子树,可以预处理每个子树的最小和最大叶子下标,询问时得到两种情况的最大最小下标,4个位置必有1个是答案。
#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define N 300005
#define MOD 998244353
using namespace std;
int n,q;
int x[N];
int e[N*15][2];
int mx[N*15],mi[N*15];
int tot = 0;
void add(int v,int i) // [0,30]
{
int u=0;
for(int j=(1<<30);j>0;j>>=1)
{
if(v&j)
{
if(!e[u][1]) e[u][1] = ++tot, mi[tot]=1e9;
u = e[u][1];
}
else
{
if(!e[u][0]) e[u][0] = ++tot, mi[tot]=1e9;
u = e[u][0];
}
mx[u] = max(mx[u], i);
mi[u] = min(mi[u], i);
}
}
bool check(int i,int a,int b)
{
if(i<1 || i>n-1) return false;
int t1 = (a^x[i])-b, t2 = (a^x[i+1])-b;
if(t1<0 && t2>0) return true;
if(t1>0 && t2<0) return true;
if(t1==0 || t2==0) return true;
return false;
}
int cal(int a,int b)
{
int mxg=0, mig=1e9, mxl=0, mil=1e9;
int u=0;
bool ok = true;
for(int j=(1<<30);j>0;j>>=1)
{
int sta=((a&j)>0), stb=((b&j)>0);
if(stb==0)
{
if(e[u][1^sta]) // >
{
mxg = max(mxg, mx[e[u][1^sta]]);
mig = min(mig, mi[e[u][1^sta]]);
}
if(e[u][sta]) u = e[u][sta];
else {ok = false; break;}
}
else
{
if(e[u][sta]) // <
{
mxl = max(mxl, mx[e[u][sta]]);
mil = min(mil, mi[e[u][sta]]);
}
if(e[u][1^sta]) u = e[u][1^sta];
else {ok = false; break;}
}
}
if(ok) return (mi[u]==n)?(mi[u]-1):mi[u];
if(mxg && mxl)
{
if(check(mig-1,a,b)) return mig-1;
if(check(mil-1,a,b)) return mil-1;
if(check(mxl,a,b)) return mxl;
if(check(mxg,a,b)) return mxg;
else {cout<<mig<<' '<<mxg<<' '<<mil<<' '<<mxl<<'\n'; return 114514;}
}
else return -1;
}
void solve()
{
cin>>n>>q;
for(int i=1;i<=n;++i)
{
cin>>x[i];
add(x[i],i);
}
// cout<<tot<<'\n';
while(q--)
{
int a,b;
cin>>a>>b;
cout<<cal(a,b)<<'\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}