2024 Autumn Training #2 CG (by hzy)

DP_PTSD發表於2024-09-28

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;
}

相關文章