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;
}
posted @ 2024-09-28 21:52  DP_PTSD  阅读(4)  评论(0编辑  收藏  举报