AtCoder Beginner Contest 437(ABCDEF)

前言

感觉还是科技树不够,E没有缩点的思维拿堆硬搞调了半天,F压根不知道这个trick......

一、A - Feet

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define INF 1e9
#define INFLL 1e18ll
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    int a,b;
    cin>>a>>b;

    cout<<12*a+b<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

没啥好说的,直接计算即可。

这应该是最简单的A了()

二、B - Tombola

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define INF 1e9
#define INFLL 1e18ll
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    int h,w,n;
    cin>>h>>w>>n;
    vector<vector<int>>g(h+1,vector<int>(w+1));
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            cin>>g[i][j];
        }
    }

    set<int>st;
    for(int i=1,x;i<=n;i++)
    {
        cin>>x;
        st.insert(x);
    }

    int ans=0;
    for(int i=1;i<=h;i++)
    {
        int sum=0;
        for(int j=1;j<=w;j++)
        {
            if(st.find(g[i][j])!=st.end())
            {
                sum++;
            }
        }

        ans=max(ans,sum);
    }
    cout<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

因为数据范围不是很大,所以可以直接枚举。

那么就是把b[i]中的每个数存set,然后考察每一行,在set中就增加,最后求最大值即可。

三、C - Reindeer and Sleigh 2

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define INF 1e9
#define INFLL 1e18ll
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    int n;
    cin>>n;
    vector<vector<ll>>a(n+1,vector<ll>(2));
    ll sum=0;
    for(int i=1;i<=n;i++)
    {
        //w p
        cin>>a[i][0]>>a[i][1];
        sum+=a[i][0];
    }

    sort(a.begin()+1,a.end(),[&](vector<ll>&x,vector<ll>&y)
    {
        return x[0]+x[1]>y[0]+y[1];
    });

    vector<ll>suf(n+2);
    for(int i=n;i>=1;i--)
    {
        suf[i]=suf[i+1]+a[i][0];
    }

    ll pre=0;
    for(int i=1;i<=n;i++)
    {
        pre+=a[i][1];

        if(pre>=suf[i+1])
        {
            cout<<n-i<<endl;
            return ;
        }
    }
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

这个贪心没猜到直接给我硬控了一会儿......

多举几个例子观察可以发现,选出来坐雪橇的驯鹿肯定既想让其重量小,也想让其力量小,所以可以考虑根据重量+力量从大到小排序,然后维护一下重量的后缀和。那么在从左往右扫的过程中,当找到第一个前缀力量大于等于后缀重量的位置,即最小拉车的个数,就可以输出了。

四、D - Sum of Differences

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define INF 1e9
#define INFLL 1e18ll
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

const int MOD=998244353;

int bs(int v,vector<ll>&a)
{
    int l=1;
    int r=a.size()-1;
    int m;
    int ans=0;
    while(l<=r)
    {
        m=l+r>>1;

        if(a[m]<=v)
        {
            ans=m;
            l=m+1;
        }
        else
        {
            r=m-1;
        }
    }
    return ans;
}

void solve()
{
    int n,m;
    cin>>n>>m;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    vector<ll>b(m+1);
    for(int i=1;i<=m;i++)
    {
        cin>>b[i];
    }

    sort(b.begin()+1,b.end());

    vector<ll>pre(m+1);
    for(int i=1;i<=m;i++)
    {
        pre[i]=(pre[i-1]+b[i])%MOD;
    }

    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int pos=bs(a[i],b);

        ans=(ans+( (pos*a[i]%MOD-pre[pos]+MOD)%MOD+((pre[m]-pre[pos]+MOD)%MOD-(m-pos)*a[i]%MOD+MOD)%MOD)%MOD )%MOD;
    }
    cout<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

牛魔前缀和没取模还给我WA了一发......

这题确实一眼二分吧()考虑对b数组排序后,构建前缀和数组。之后扫一遍a数组,每次去b数组里二分找小于等于a[i]的最右位置。由于左侧都是小于等于a[i]的,右侧都是大于等于a[i]的,所以就可以把绝对值去掉,直接计算对答案的贡献。

五、E - Sort Arrays

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define INF 1e9
#define INFLL 1e18ll
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    int n;
    cin>>n;
    vector<int>x(n+1);
    vector<int>y(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>x[i]>>y[i];
    }

    vector<vector<pii>>g(n+1);
    for(int i=1;i<=n;i++)
    {
        g[x[i]].push_back({y[i],i});
    }    

    for(int i=1;i<=n;i++)
    {
        sort(g[i].begin(),g[i].end());
    }

    vector<int>ans(n+1);
    int fill=1;

    auto dfs=[&](auto &&self,vector<int>cur)->void
    {
        priority_queue<pii,vector<pii>,greater<pii>>heap;

        for(auto u:cur)
        {
            for(auto [w,v]:g[u])
            {
                heap.push({w,v});
            }
        }

        while(!heap.empty())
        {
            auto cur=heap.top().first;

            vector<int>nx;
            while(!heap.empty()&&heap.top().first==cur)
            {
                auto [w,v]=heap.top();
                heap.pop();

                nx.push_back(v);
                ans[fill++]=v;
            }

            self(self,nx);
        }
    };

    vector<int>cur={0};
    dfs(dfs,cur);

    for(int i=1;i<=n;i++)
    {
        cout<<ans[i]<<" ";
    }
    cout<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

这是怎么做到又像dfs又像bfs的......

首先考虑对于每个操作,建立一条x[i]到i边权为y[i]的边,那么结果肯定构成一棵树,可以看作一棵字典树。之后,对于一条链上的节点,由于前缀相同,长度递增,所以肯定是先输出层数更低的节点。而对于相同长度的节点,肯定是先输出边权更小的节点。

所以可以先对每个g[i]排序,即保证每个节点的孩子节点满足从小到大。之后,考虑带着一张表去dfs,这张表记录上一层处理过的点。所以每次可以考虑用小根堆去维护表中所有点的孩子节点,那么就是每次考虑边权相同的一批点。因为这些点长度最小,所以就先输出这些点,然后带着这些点去调递归。

其实可以直接用map的,这实现太史了......

六、F - Manhattan Christmas Tree 2

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

const int MAXN=2e5+5;

vector<array<ll,2>>a(MAXN);

vector<ll>LMIN(MAXN<<2);
vector<ll>RMIN(MAXN<<2);
vector<ll>LMAX(MAXN<<2);
vector<ll>RMAX(MAXN<<2);

void up(int i)
{
    int l=i<<1,r=i<<1|1;
    LMIN[i]=min(LMIN[l],LMIN[r]);
    LMAX[i]=max(LMAX[l],LMAX[r]);
    RMIN[i]=min(RMIN[l],RMIN[r]);
    RMAX[i]=max(RMAX[l],RMAX[r]);
}

void build(int l,int r,int i)
{
    if(l==r)
    {
        LMIN[i]=LMAX[i]=a[l][0]+a[l][1];
        RMIN[i]=RMAX[i]=a[l][0]-a[l][1];
    }
    else
    {
        int m=l+r>>1;
        build(l,m,i<<1);
        build(m+1,r,i<<1|1);
        up(i);
    }
}

void change(int jobi,int jobx,int joby,int l,int r,int i)
{
    if(l==r)
    {
        LMIN[i]=LMAX[i]=jobx+joby;
        RMIN[i]=RMAX[i]=jobx-joby;
    }
    else
    {
        int m=l+r>>1;

        if(jobi<=m)
        {
            change(jobi,jobx,joby,l,m,i<<1);
        }
        else   
        {
            change(jobi,jobx,joby,m+1,r,i<<1|1);
        }

        up(i);
    }
}

array<ll,4> query(int jobl,int jobr,int l,int r,int i)
{
    if(jobl<=l&&r<=jobr)
    {
        return {LMIN[i],RMIN[i],LMAX[i],RMAX[i]};
    }

    int m=l+r>>1;

    array<ll,4>left={INFLL,INFLL,-INFLL,-INFLL};
    if(jobl<=m)
    {
        left=query(jobl,jobr,l,m,i<<1);
    }
    array<ll,4>right={INFLL,INFLL,-INFLL,-INFLL};
    if(m+1<=jobr)
    {
        right=query(jobl,jobr,m+1,r,i<<1|1);
    }

    return {min(left[0],right[0]),min(left[1],right[1]),max(left[2],right[2]),max(left[3],right[3])};
}

void solve()
{
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i][0]>>a[i][1];
    }

    build(1,n,1);

    int op,i,x,y,l,r;
    while(q--)
    {
        cin>>op;

        if(op==1)
        {
            cin>>i>>x>>y;

            change(i,x,y,1,n,1);
        }
        else
        {
            cin>>l>>r>>x>>y;

            auto res=query(l,r,1,n,1);

            ll xx=x+y;
            ll yy=x-y;

            cout<<max({
                xx-res[0],yy-res[1],res[2]-xx,res[3]-yy
            })<<endl;
        }
    }
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

直接维护曼哈顿距离的话,单点修改操作其实不太好实现,那么就需要考虑进行转化。

对于曼哈顿距离,常见的转化就是将其转化为切比雪夫距离。首先,两点(a,b)和(x,y)之间的切比雪夫距离就是max(abs(a-x),abs(b-y))。直接说结论,对于点(x,y),将其转化为点(x+y,x-y)后,此时的切比雪夫距离就是原来点的曼哈顿距离。

那么有了这个trick,将所有点转化以后,对于范围查询操作,只需要维护[l,r]范围内横纵坐标的最大最小值,看给定的点和哪个值的差值最大即可。那么这个值就是当前坐标下切比雪夫距离的最大值,也就是原坐标下曼哈顿距离的最大值。

总结

感觉现在需要点科技树了......

END

相关推荐
郝学胜-神的一滴2 小时前
Linux 下循环创建多线程:深入解析与实践指南
linux·服务器·c++·程序人生·算法·设计模式
superman超哥2 小时前
仓颉语言中异常处理入门的深度剖析与工程实践
c语言·开发语言·c++·python·仓颉
ss2732 小时前
线程池优雅关闭:线程池生命周期管理:四种关闭策略的实战对比
java·jvm·算法
天呐草莓2 小时前
热传导方程
算法·matlab
wxdlfkj2 小时前
从坐标系重构到算法收敛:以高性能LTP传感器突破圆周分布孔组位置度的即时检测瓶颈
算法·重构
不能只会打代码2 小时前
蓝桥杯--生命之树(Java)
java·算法·蓝桥杯·动态规划·贪心
MobotStone2 小时前
三步高效拆解顶刊论文
算法
CreasyChan2 小时前
unity射线与几何检测 - “与世界的交互”
算法·游戏·3d·unity·数学基础
leiming62 小时前
C++ 类模板对象做函数参数
开发语言·c++·算法