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

相关推荐
我在人间贩卖青春6 小时前
C++之this指针
c++·this
云姜.6 小时前
java多态
java·开发语言·c++
CoderCodingNo6 小时前
【GESP】C++五级练习题 luogu-P1865 A % B Problem
开发语言·c++·算法
陳10306 小时前
C++:红黑树
开发语言·c++
大闲在人6 小时前
7. 供应链与制造过程术语:“周期时间”
算法·供应链管理·智能制造·工业工程
一切尽在,你来6 小时前
C++ 零基础教程 - 第 6 讲 常用运算符教程
开发语言·c++
小熳芋6 小时前
443. 压缩字符串-python-双指针
算法
Charlie_lll6 小时前
力扣解题-移动零
后端·算法·leetcode
chaser&upper6 小时前
矩阵革命:在 AtomGit 解码 CANN ops-nn 如何构建 AIGC 的“线性基石”
程序人生·算法
weixin_499771556 小时前
C++中的组合模式
开发语言·c++·算法