20241001

赛时得分

题目 A B C D 总分 排名 比例
满分 100 100 100 100 400 163 100%
得分 50 57 20 0 127 89 54.6%

A. 好伙伴数(100/100)

\(\text{30%}\) 得分做法,直接 \(n^2\) 枚举即可,但考试的时候写了一坨,甚至还挂了 \(\text{10pts}\)

\(\text{60%}\) 得分做法,也就是只有两种数字的性质,我们分为只有 1、只有 2、1 和 2 都有这三种情况。对于分别只有一个数字的情况,我们直接选择乘法原理(等差数列也一样)统计答案数,对于两个数都有的情况,我们先乘法原理,再每个乘上其他情况的方案数总和即可。

挂一下赛时的 \(\text{50pts}\) 代码:

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e6+1;
ll n,a[11][N],c[11];
set<pair<ll,ll> > s;
string str[N];
bool ok=1;
void change(ll i,ll res,ll c)
{
    for(int j=1;j<=c;j++)
    {
        if(a[res][j]==i) continue;
        s.insert({min(a[res][j],i),max(a[res][j],i)});
    }
    a[res][++c]=i;
    return;
}
int main()
{
    freopen("friend.in","r",stdin);
    freopen("friend.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>str[i];
        for(int j=0;j<str[i].length();j++)
        {
            if(str[i][j]!='1' and str[i][j]!='2') ok=0;
        }
    }
    if(ok==0)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<str[i].length();j++)
            {
                string num="";
                num+=str[i][j];
                ll res=stoi(num);
                change(i,res,c[res]);
                c[res]++;
            }
        }
        cout<<s.size();
    }
    else
    {
        ll cnt1=0,cnt2=0,cnt3=0,ans=0;
        for(int i=1;i<=n;i++)
        {
            bool vis1=0,vis2=0;
            for(int j=0;j<str[i].length();j++)
            {
                if(str[i][j]=='1') vis1=1;
                else if(str[i][j]=='2') vis2=1;
            }
            if(vis1==1 and vis2==0) cnt1++;
            else if(vis1==1 and vis2==1) cnt3++;
            else cnt2++;
        }
        ans+=cnt3*(cnt1+cnt2);
        ans+=(1+cnt1-1)*(cnt1-1)/2;
        ans+=(1+cnt2-1)*(cnt2-1)/2;
        ans+=(1+cnt3-1)*(cnt3-1)/2;
        cout<<ans;
    }
    return 0;
}

\(\text{100%}\) 得分做法,一种状态压缩的套路,就是二进制状压,我们考虑直接将一个数字的所含的数字表示成一个二进制形式,每一位上有对应数字就是 1,没有就是 0。然后我们会得到一个二进制数,再转化为十进制,就可以很简单得到一个转化后的压缩数,不难发现所有的压缩数最大也只能达到 \(2^{10}=1024\),所以我们就统计个数,然后放心 \(\mathcal{O}(1024^2)\) 枚举即可,对于一样的性质直接乘法原理,剩下只要按位与的结果是 1 就可以乘起来。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e6+1;
ll n,a[N],cnt[N],ans;
ll convert(ll x)
{
    ll res=0;
    bool vis[11];
    memset(vis,0,sizeof(vis));
    while(x)
    {
        vis[x%10]=1;
        x/=10;
    }
    for(int i=0;i<=9;i++)
    {
        if(vis[i]==1) res+=pow(2,i);
    }
    return res;
}
int main()
{
    freopen("friend.in","r",stdin);
    freopen("friend.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        cnt[convert(a[i])]++;
    }
    for(int i=0;i<=1024;i++)
    {
        for(int j=i;j<=1024;j++)
        {
            if(i==j) ans+=cnt[i]*(cnt[i]-1)>>1;
            else if(i&j) ans+=cnt[i]*cnt[j];
        }
    }
    cout<<ans;
    return 0;
}

B. 武装士兵(57/100)

\(\text{20%}\) 得分做法,是 \(A_i=0\) 这一档部分分,我们直接将所有价格从小到大排序,直接能选多少选多少就行了,最大价格一定是 0,因为士兵自己没钱。

\(\text{50%}\) 得分做法,再加上 \(S=0\) 这一档,考虑我们将所有价格扔到一个 multiset 里,然后按照人手里的钱数从大到小 lower_bound,如果在最开始就是,并且不等于你的钱数,就说明你买不起,过掉。其他的如果 find 结果是 1,就直接买,如果 lower_bound 不是开头,我们走一下 lower_bound 然后左移一位就是能买的起的最大值了。这时候我们注意要统计一下最大钱数就行了。

\(\text{57%}\) 得分做法,我的决策其实是有问题的,但是精度还挺可观的,迷迷糊糊水过了两个点。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e5+1;
ll n,m,s,a[N],b[N];
bool ok=0;
bool cmp(ll a,ll b)
{
    return a>b;
}
int main()
{
    freopen("soldier.in","r",stdin);
    freopen("soldier.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>s;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        if(a[i]!=0) ok=1;
    }
    for(int i=1;i<=m;i++) cin>>b[i];
    if(ok==0)
    {
        sort(b+1,b+m+1);
        ll sum=0;
        for(ll i=1;i<=m;i++)
        {
            sum+=b[i];
            if(sum>s)
            {
                cout<<min(i-1,n)<<" 0";
                return 0;
            }
        }
        cout<<n<<" 0";
        return 0;
    }
    if(s==0)
    {
        sort(a+1,a+n+1,cmp);
        ll cnt=0,ans=0;
        multiset<ll> val;
        for(int i=1;i<=m;i++) val.insert(b[i]);
        for(int i=1;i<=n;i++)
        {
            if(val.find(a[i])==val.end() and val.lower_bound(a[i])==val.begin()) continue;
            else
            {
                if(val.find(a[i])!=val.end())
                {
                    val.erase(val.find(a[i]));
                    cnt++;
                    ans=max(ans,a[i]);
                }
                else
                {
                    cnt++;
                    ans=max(ans,*--val.lower_bound(a[i]));
                    val.erase(--val.lower_bound(a[i]));
                }
            }
        }
        cout<<cnt<<" "<<ans;
        return 0;
    }
    else
    {
        sort(a+1,a+n+1,cmp);
        ll cnt=0,ans=0;
        multiset<ll> val;
        for(int i=1;i<=m;i++) val.insert(b[i]);
        for(int i=1;i<=n;i++)
        {
            if(val.find(a[i])==val.end() and val.lower_bound(a[i])==val.begin())
            {
                s-=(*val.lower_bound(a[i])-a[i]);
                if(s>=0)
                {
                    cnt++;
                    val.erase(val.lower_bound(a[i]));
                }
            }
            else
            {
                if(val.find(a[i])!=val.end())
                {
                    val.erase(val.find(a[i]));
                    cnt++;
                }
                else
                {
                    cnt++;
                    val.erase(--val.lower_bound(a[i]));
                }
            }
        }
        cout<<cnt<<" "<<0;
        return 0;
    }
    return 0;
}

C. 送蛋糕(50/100)

\(\text{20%}\) 得分做法,我们直接 next_permutation 枚举全排列,依题意算一下最大收益即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1,inf=2147483647;
ll n,m,t[N],l[N],k,tk,lk,a[N],maxn=-inf,ans,tim;
int main()
{
    freopen("cake.in","r",stdin);
    freopen("cake.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>t[i]>>l[i];
    for(int i=1;i<=n;i++) a[i]=i;
    do
    {
        ans=0,tim=0;
        for(int i=1;i<=n;i++)
        {
            tim+=l[a[i]];
            ans+=(t[i]-tim);
        }
        maxn=max(maxn,ans);
    }while(next_permutation(a+1,a+n+1));
    cout<<maxn<<endl;
    while(m--)
    {
        cin>>k>>tk>>lk;
        t[k]=tk,l[k]=lk;
        for(int i=1;i<=n;i++) a[i]=i;
        maxn=-inf;
        do
        {
            ans=0,tim=0;
            for(int i=1;i<=n;i++)
            {
                tim+=l[a[i]];
                ans+=(t[i]-tim);
            }
            maxn=max(maxn,ans);
        }while(next_permutation(a+1,a+n+1));
        cout<<maxn<<endl;
    }
    return 0;
}

\(\text{40%}\) 做法,终究还是败给了贪心。考虑贪心,我们应该优先做的任务是耗时短的任务(正确性还没证出来,但他就是对的)。然后直接模拟就完事了。

老师,我家 next_permutation 为什么比不过他家的贪心。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1;
ll n,m,k,tk,lk,ans,tim;
struct lhm
{
    ll t,l;
}a[N],b[N];
bool cmp(lhm a,lhm b)
{
    if(a.l==b.l) return a.t<b.t;
    return a.l<b.l;
}
int main()
{
    freopen("cake.in","r",stdin);
    freopen("cake.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].t>>a[i].l;
        b[i].t=a[i].t;
        b[i].l=a[i].l;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        tim+=a[i].l;
        ans+=(a[i].t-tim);
    }
    cout<<ans<<endl;
    while(m--)
    {
        ans=0,tim=0;
        cin>>k>>tk>>lk;
        b[k].t=tk,b[k].l=lk;
        for(int i=1;i<=n;i++)
        {
            a[i].t=b[i].t;
            a[i].l=b[i].l;
        }
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            tim+=a[i].l;
            ans+=(a[i].t-tim);
        }
        cout<<ans<<endl;
    }
    return 0;
}

\(\text{50%}\) 得分做法,做 \(L_i\) 相等的那一档部分分。其实说实话会了 40 分再做这档就非常简单了,我们只需要在线修改记录的 \(ans\),随便就能把修改操作复杂度降到 \(\mathcal{O}(m)\)

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1;
ll n,m,k,tk,lk,ans,tim;
bool ok=1;
struct lhm
{
    ll t,l;
}a[N],b[N];
bool cmp(lhm a,lhm b)
{
    if(a.l==b.l) return a.t<b.t;
    return a.l<b.l;
}
int main()
{
    freopen("cake.in","r",stdin);
    freopen("cake.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].t>>a[i].l;
        b[i].t=a[i].t;
        b[i].l=a[i].l;
        if(a[i].l!=a[1].l) ok=0;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        tim+=a[i].l;
        ans+=(a[i].t-tim);
    }
    cout<<ans<<endl;
    if(ok==1)
    {
        while(m--)
        {
            cin>>k>>tk>>lk;
            ans+=(tk-b[k].t);
            b[k].t=tk,b[k].l=lk;
            cout<<ans<<endl;
        }
        return 0;
    }
    else
    {
        while(m--)
        {
            ans=0,tim=0;
            cin>>k>>tk>>lk;
            b[k].t=tk,b[k].l=lk;
            for(int i=1;i<=n;i++)
            {
                a[i].t=b[i].t;
                a[i].l=b[i].l;
            }
            sort(a+1,a+n+1,cmp);
            for(int i=1;i<=n;i++)
            {
                tim+=a[i].l;
                ans+=(a[i].t-tim);
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}
posted @ 2024-10-01 12:18  Lithium_Chestnut  阅读(11)  评论(0)    收藏  举报