AtCoder Beginner Contest 441(ABCDEF)

前言

牛魔的D没看到L的限制卡了半天,把E秒了回来才过的T^T

一、A - Black Square

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

void solve()
{
    int p,q,x,y;
    cin>>p>>q>>x>>y;

    if(p<=x&&x<=p+100-1&&q<=y&&y<=q+100-1)
    {
        Yes;
    }
    No;
}

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

没啥好说的,判断一下边界输出即可,注意边界要 -1 即可。

二、B - Two Languages

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

void solve()
{
    int n,m;
    cin>>n>>m;
    string s,t;
    cin>>s>>t;
    s=" "+s;
    t=" "+t;

    set<char>a,b;
    for(int i=1;i<=n;i++)
    {
        a.insert(s[i]);
    }
    for(int i=1;i<=m;i++)
    {
        b.insert(t[i]);
    }

    int q;
    cin>>q;
    string w;
    while(q--)
    {
        cin>>w;

        bool A=true,B=true;
        for(auto c:w)
        {
            if(a.find(c)==a.end())
            {
                A=false;
            }
            if(b.find(c)==b.end())
            {
                B=false;
            }
        }

        if(A^B)
        {
            if(A)
            {
                cout<<"Takahashi"<<endl;
            }
            else
            {
                cout<<"Aoki"<<endl;
            }
        }
        else
        {
            cout<<"Unknown"<<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;
}

还是经典 abc 的 stl 题,就是把两串出现的字符全存set里,然后对于每个字符串,考察是否全是A里的字符或全是B里的字符。若只有其中一个满足,即异或为 true,那么就可以判断出来输出,否则就是Unknown。

三、C - Sake or Water

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

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

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

    ll sum=0;
    for(int i=k;i>=1;i--)
    {
        sum+=a[i];

        if(sum>=x)
        {
            cout<<k-i+1+n-k<<endl;
            return ;
        }
    }

    cout<<-1<<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了一发......

首先,如果想让喝的次数最少,那么肯定是先喝容量大的。之后,因为想要让喝的杯数尽可能地多,那么肯定是让容量最大的n-k个杯子里全是水。所以在从小到大排序后,前k个杯子就是有酒的杯子。那么从第k个杯子开始从大往小枚举,当碰到累加和大于等于 x 的第一个位置就可以输出了,否则就说明没法喝满 x 。

没看到L的限制卡了半天,过了E回来才发现......

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

void solve()
{
    int n,m,l,s,t;
    cin>>n>>m>>l>>s>>t;
    vector<vector<pll>>g(n+1);
    for(int i=1,u,v,w;i<=m;i++)
    {
        cin>>u>>v>>w;
        g[u].push_back({v,w});
    }

    vector<vector<vector<ll>>>dp(n+1,vector<vector<ll>>(l+1));

    auto dfs=[&](auto &&self,int u,int step,int cur)->void
    {
        if(step==l)
        {
            return ;
        }

        for(auto [v,w]:g[u])
        {
            dp[v][step+1].push_back(cur+w);
            self(self,v,step+1,cur+w);
        }
    };

    dp[1][0].push_back(0);
    dfs(dfs,1,0,0);

    vector<int>ans;
    for(int i=1;i<=n;i++)
    {
        for(auto x:dp[i][l])
        {
            if(s<=x&&x<=t)
            {
                ans.push_back(i);
                break;
            }
        }
    }

    for(auto x:ans)
    {
        cout<<x<<" ";
    }
}

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

注意到L的范围很小,那么就可以直接暴力定义dp[i][j]为从1号节点到 i 号节点所有长度为 j 的路径的权值和。

那么之后就可以直接带着当前节点u,之前走过的长度step和当前权值和去dfs,每次考察孩子节点,往dp[v][step+1]里push_back。最后考察每个节点长度为L的路径中所有的权值和,只要存在一个权值和在范围[S,T]内的路径,当前节点就可以加入答案。

额外的,因为说明了每个节点的外度最多为4,所以从 1 节点出发走过长度为L的路径的数量最多只有4L条,因为L很小,所以这个枚举的复杂度是可以接受的。

五、E - A > B substring

有了板子就是爽()

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

template<typename T>
struct BIT
{
    vector<T>tree;

    BIT(int n)
    {
        tree.resize(n);
    }

    int lowbit(int i)
    {
        return i&-i;
    }

    void add(int i,T v)
    {
        while(i<tree.size())
        {
            tree[i]+=v;
            i+=lowbit(i);
        }
    }

    T sum(int i)
    {
        T ans=0;
        while(i>0)
        {
            ans+=tree[i];
            i-=lowbit(i);
        }
        return ans;
    }

    T query(int l,int r)
    {
        return sum(r)-sum(l-1);
    }
};

void solve()
{
    int n;
    cin>>n;
    string s;
    cin>>s;
    s=" "+s;

    BIT<int>tree(2*n+2);
    
    int A=0,B=0;
    tree.add(A-B+n+1,1);

    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='A')
        {
            A++;
        }
        if(s[i]=='B')
        {
            B++;
        }

        ans+=tree.sum(A-B+n);
        tree.add(A-B+n+1,1);
    }
    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;
}

对于区间内A的数量大于B的数量的这个条件,还是考虑将区间问题转化成前缀问题。那么对于当前位置,若前缀A有x个,前缀B有y个,若前面某位置的前缀A有x'个,B有y'个,那么若想要这个区间符合要求,就需要满足x-x'大于y-y',移项可得x-y大于x'-y'。

所以当前位置能产生的贡献,就是前缀x-y小于当前x-y的个数,那么这个就可以考虑使用树状数组维护了。由于x-y会出现负数,那么就需要将其加上n+1偏移一下,每次统计完当前的前缀个数A和B后,计算对答案的贡献,然后更新树状数组即可。

六、F - Must Buy

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

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

    vector<vector<ll>>pre(n+1,vector<ll>(m+1));
    pre[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            pre[i][j]=pre[i-1][j];
            if(j-a[i][0]>=0)
            {
                pre[i][j]=max(pre[i][j],pre[i-1][j-a[i][0]]+a[i][1]);
            }
        }
    }
    
    vector<vector<ll>>suf(n+2,vector<ll>(m+1));
    suf[n+1][0]=0;
    for(int i=n;i>=1;i--)
    {
        for(int j=m;j>=0;j--)
        {
            suf[i][j]=suf[i+1][j];
            if(j-a[i][0]>=0)
            {
                suf[i][j]=max(suf[i][j],suf[i+1][j-a[i][0]]+a[i][1]);
            }
        }
    }

    ll ans=0;
    for(int j=0;j<=m;j++)
    {
        ans=max(ans,pre[n][j]);
    }

    for(int i=1;i<=n;i++)
    {
        ll no=0;
        for(int j=0;j<=m;j++)
        {
            ll p1=pre[i-1][j];
            ll p2=suf[i+1][m-j];

            no=max(no,p1+p2);
        }

        ll yes=0;
        ll rest=m-a[i][0];
        for(int j=0;j<=rest;j++)
        {
            ll p1=pre[i-1][j];
            ll p2=suf[i+1][rest-j];
            yes=max(yes,p1+p2);
        }
        yes+=a[i][1];

        if(yes<ans)
        {
            cout<<"C";
        }
        else if(no<ans)
        {
            cout<<"A";
        }
        else
        {
            cout<<"B";
        }
    }
}

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

首先,对于某一个商品,若选它的时候达不到最大值,那么这个商品就必然不能选。否则,即选它的时候能达到最大值,而不选的时候达不到最大值,那么这个商品就是必须选的。否则,即选不选都能达到最大值,那么就说明这个商品可选可不选。

之后,也算是一个常见的trick吧(?,就是对于需要单独讨论某个物品的问题,可以考虑构建前后缀,然后整合看最值。

所以在这个题里,可以考虑先正着跑一遍01背包构建出pre[i][j],再倒着跑一遍01背包构建出suf[i][j]。之后抓出最大值后,枚举每个物品,考虑要和不要两种情况的答案。

首先,对于不要的情况,可以枚举前缀选出的重量 j ,那么答案就是pre[i-1][j]+suf[i+1][m-j]的最大值。之后,对于要的情况,首先剩下可供选择的重量就是m-a[i][0]。然后就可以同样枚举前缀选的重量 j ,求出最大值,然后加上当前物品的价值,最后比较判断即可。

总结

保持好状态,多写题多积累,加油!!

END

相关推荐
晨非辰2 小时前
C++波澜壮阔40年|类和对象篇:拷贝构造与赋值重载的演进与实现
运维·开发语言·c++·人工智能·后端·python·深度学习
Remember_9932 小时前
【LeetCode精选算法】双指针专题一
java·数据结构·算法·leetcode
多米Domi0112 小时前
0x3f 第36天 外卖8,9,树
数据结构·python·算法·leetcode
jonyleek2 小时前
开源APS排产系统,出货计划如何成为企业降本增效的关键?
算法·开源·私有化部署·软件开发·生产排产·aps排产系统
hetao17338372 小时前
2026-01-16~19 hetao1733837 的刷题笔记
c++·笔记·算法
程序员-King.2 小时前
day153—回溯—子集(LeetCode-78)
算法·leetcode·回溯·递归
玖釉-2 小时前
[Vulkan 学习之路] 29 - 加载模型 (Loading Models)
c++·windows·图形渲染
MicroTech20252 小时前
突破C2Q瓶颈,MLGO微算法科技高性能可重构计算机实现量子算法真实级仿真,推动量子仿真进入新阶段
科技·算法·重构
HelloWorld1024!2 小时前
C++中链表的虚拟头结点:应用场景与使用时机
网络·c++·链表