某场模拟赛

T2
AT_abc427_e Wind Cleaning
神秘搜索
直接模拟移动障碍物的过程搜索,用 set 记录所有障碍物的位置,对于每次移动改变所有障碍物的位置,超出网格则删掉,然后标记搜过的状态,用 map 记录每一个 set 的状态是否被访问过即可。
注意 map 可以存储各种状态但是 unordered_map 不行。set 某种程度上同理,总之如果使用 unordered 报错的话就换成普通 STL 容器即可。

#include <bits/stdc++.h>
using namespace std;
int n,m,stx,sty;
typedef pair<int,int> pii;
set<pii> st,w;
int dx[4] = {0,-1,0,1};
int dy[4] = {1,0,-1,0};
struct node
{
    set<pii> s;
    int stp;
};
queue <node> q;
map<set<pii>,bool> mp;
char s;
int main()
{
    cin >> n >> m;
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++)
        {
            cin >> s;
            if (s == 'T') stx = i,sty = j;
            else if (s == '#') st.insert({i,j});
        }
    q.push({st,0});
    while(q.size())
    {
        node t = q.front(); q.pop();
        if (mp[t.s]) continue;
        mp[t.s] = 1;
        if (!t.s.size())
        {
            cout << t.stp << "\n";
            return 0;
        }
        for (int i = 0;i < 4;i++)
        {
            w.clear();
            bool f = 1;
            for (auto it:t.s)
            {
                int tx = it.first+dx[i];
                int ty = it.second+dy[i];
                if (tx<1||ty<1||tx>n||ty>m) continue;
                if (tx == stx && ty == sty)
                {
                    f = 0;
                    break;
                }
                w.insert({tx,ty});
            }
            if (f) q.push({w,t.stp+1});
        }
    }
    cout << -1;
    return 0;
}

T4
AT_abc425_g Sum of Min of XOR
01trie
注意到若 \(m\) 的范围比较小的话是可以直接枚举在 01-trie 上匹配的,范围太大启发我们对于一个区间内的数字同时操作,具体地,注意到若 \(m\) 的范围在 \([0,2^k-1]\),即对应一个满二叉树,那么每一棵子树内的数字个数是一定的,此时可以便利的计算每一位上的贡献:若当前点子树内共有 \(cnt\) 个数字,当前点只有一个儿子时,另一个儿子子树内的所有数字都会在此处造成贡献,所以共造成 \(cnt/2*(1<<i)\) 的贡献,\(i\) 表示当前是第几位。
但是 \(m\) 的范围并非如此理想,于是稍微使用一点小巧思,将 \(m\) 分为若干个长度为 \(2^k\) 的区间,对于每一个 \([x,x+2^k-1]\),找到这段区间的祖先,子树内就满足数字平均了,dfs即可。

#include <bits/stdc++.h>
using namespace std;
int n,m,tr[50000010][2],tot,x;
long long ans;
void insert(int x)
{
    int p = 0;
    for (int i = 30;i >= 0;i--)
    {
        int s = x>>i&1;
        if (!tr[p][s]) tr[p][s] = ++tot;
        p = tr[p][s];
    }
}
void dfs(int p,long long cnt,int dep)
{
    if (tr[p][0] && tr[p][1])
    {
        dfs(tr[p][0],cnt/2,dep-1);
        dfs(tr[p][1],cnt/2,dep-1);
    }
    else if (tr[p][0] && !tr[p][1])
    {
        ans += cnt*(1<<dep)/2;
        dfs(tr[p][0],cnt,dep-1);
    }
    else if (!tr[p][0] && tr[p][1])
    {
        ans += cnt*(1<<dep)/2;
        dfs(tr[p][1],cnt,dep-1);
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1;i <= n;i++)
    {
        cin >> x;
        insert(x);
    }
    for (int i = 30;i >= 0;i--)
        if (m>>i&1)
        {
            long long p = 0,cnt = 1<<i;
            for (int j = 30;j >= i;j--)
            {
                int s = m>>j&1;
                if (j == i) s = 0;
                if (tr[p][s]) p = tr[p][s];
                else
                {
                    p = tr[p][s^1];
                    ans += cnt*(1<<j);
                }
            }
            dfs(p,cnt,i-1);
        }
    cout << ans << "\n";
    return 0;
}

T5
AT_abc426_g Range Knapsack Query
猫树分治
一种比较高级的分治,关于分治可以看例题P7883,简单来说就是以一种类似归并排序的结构,每次处理跨过中间点的答案,或者有其他分治方式我不知道,然后就可以在 \(O(n\log n)\) 的时间内处理完所有的答案。
猫树是一种比较高级的线段树,支持静态序列的区间查询,由于普通线段树每次查询要经过 \(O(\log n)\) 次合并,当维护的信息合并复杂度较高时,普通线段树就会比较慢,但是猫树来了。猫树的特点是对于线段树每个节点对应区间 \([l,r]\) 上找到中点 \(mid\),然后处理出从 \(mid\) 出发向左向右各自延伸的区间信息,则对于每一个查询区间 \([ql,qr]\),找到线段树上第一个完全包含 \([ql,qr]\) 的节点,或者也可以认为是 \(ql,qr\) 两个点的 lca,在此处进行合并即可。这样子合并只会发生 \(O(1)\) 次,可以降一个老哥。
当然猫树分治和线段树没关系,重点在于从中点出发向左右扩展的思想。对于每一个分治区间,考虑处理跨过中点的询问,处理出从中点向左右扩展的区间背包,总共是 \(O(nt\log n)\) 的复杂度,\(t\) 表示背包容量。那么每一个查询只需要合并两个区间即可,预处理出背包的前缀 \(\max\),每计算一个询问的复杂度只有 \(O(t)\)。总复杂度为 \(O(mt)\)
整道题的复杂度为 \(O(nt\log n+mt)\)
注意猫树分治要满足信息的可合并性,保证合并两个区间的复杂度很重要(我想)。比如背包DP可以看作 \(\{\max,+\}\) 卷积的生成函数。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,f[20010][510],x[510],y[510],h[20010],w[20010],ans[200010],tmp[510];
struct que
{
    ll l,r,m,id;
    bool operator<(const que &x) const
    {
        return r<x.r;
    }
};
vector<que> qry;
void dac(int l,int r,vector<que> g)
{
    if (l == r)
    {
        for (auto it:g) ans[it.id] = it.m>=h[l]?w[l]:0;
        return;
    }
    int mid = l+r>>1;
    vector<que> gl,gr,aq;
    for (auto it:g)
    {
        if (it.r <= mid) gl.push_back(it);
        else if (it.l > mid) gr.push_back(it);
        else aq.push_back(it);
    }
    dac(l,mid,gl); dac(mid+1,r,gr);
    sort(aq.begin(),aq.end());
    for (int i = 0;i <= 500;i++) f[mid+1][i] = 0;
    for (int i = mid;i >= l;i--)
    {
        for (int j = 500;j >= h[i];j--) f[i][j] = max(f[i+1][j],f[i+1][j-h[i]]+w[i]);
        for (int j = h[i]-1;j >= 0;j--) f[i][j] = f[i+1][j];
    }
    int pos = mid+1;
    for (int i = 0;i <= 500;i++) x[i] = y[i] = tmp[i] = 0;
    for (auto it:aq)
    {
        while(pos <= it.r)
        {
            for (int i = 500;i >= h[pos];i--)
                tmp[i] = max(tmp[i],tmp[i-h[pos]]+w[pos]);
            pos++;
        }
        for (int i = 1;i <= 500;i++)
        {
            x[i] = max(x[i-1],f[it.l][i]);
            y[i] = max(y[i-1],tmp[i]);
        }
        for (int i = 0;i <= it.m;i++) ans[it.id] = max(ans[it.id],x[i]+y[it.m-i]);
    }
}
inline int read()
{
    int x = 0;
    char ch = getchar();
    while(ch<'0'||ch>'9') ch = getchar();
    while(ch>='0'&&ch<='9')
    {
        x = (x<<1)+(x<<3)+(ch^48);
        ch = getchar();
    }
    return x;
}
int main()
{
    n = read();
    for (int i = 1;i <= n;i++)
    {
        h[i] = read(); w[i] = read();
    }
    m = read();
    for (int i = 0;i < m;i++) qry.push_back({read(),read(),read(),i});
    dac(1,n,qry);
    for (int i = 0;i < m;i++) cout << ans[i] << "\n";
    return 0;
}
posted @ 2025-11-07 11:47  BAMBANG  阅读(5)  评论(0)    收藏  举报
signature: { enable: true, contents: [ "This theme is built with awescnb.", "console.log(🍺);", ], },