AtCoder Grand Contest 053

链接

B. Taking the middle

题解

首先可以把序列按 n 划开,第一个序列从 n1,第二个序列从 n+12n。可以发现,每次操作中对手取出较多的那个序列的最开头元素。

容易证明,一组选择是合法的当且仅当对于任意 i,两个序列的前 i 个数字中至少有 i 个被对手选取了。这样直接从前往后带悔贪心即可。每次让对手选出当前最小的没有被选的元素即可。

复杂度 O(nlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=400010;
int a[N];
priority_queue<int,vector<int>,greater<int>>q;
int main()
{
    int n;scanf("%d",&n);
    long long ans=0;
    for(int i=1;i<=n*2;i++) scanf("%d",&a[i]),ans+=a[i];
    for(int i=n,j=i+1;i;i--,j++) q.push(a[i]),q.push(a[j]),ans-=q.top(),q.pop();
    printf("%lld\n",ans);
    return 0;
}

C. Random Card Game

题解

首先 2n 所在的堆一定会被保留下来,所以相当于要用最小的步数移走另一堆卡片。考虑左边的每张卡片,它需要匹配右边的一张比他大卡片。容易证明,答案是左边每张卡片位置(从上往下)减去右边比他大的第一张卡片位置之差的最大值。

枚举这个值 d,考虑计算答案 d 的方案数。直接计算有点麻烦,但是容易发现一点是,如果一张卡片的上方有一张比他大的卡片,那么这张卡片一定不会成为限制。所以从上往下考虑卡片 i,要求就是当前牌堆 1i 和另一边牌堆 1i+d 中当前位置 i 不是最大的。

由于要求之间相互独立,所以答案是每个位置答案相乘。直接预处理双阶乘及其逆元即可。复杂度 O(n)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2000010,mod=1000000007;
int ksm(int a,int b=mod-2)
{
    int r=1;
    for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) r=1ll*r*a%mod;
    return r;
}
int fac[N],inv[N],iv[N],fac2[N],inv2[N];
void init(int n=N-10)
{
    for(int i=fac[0]=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
    inv[n]=ksm(fac[n]);
    for(int i=n-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
    iv[1]=1;for(int i=2;i<=n;i++) iv[i]=1ll*(mod-mod/i)*iv[mod%i]%mod;
    fac2[0]=fac2[1]=1;
    for(int i=2;i<=n;i++) fac2[i]=1ll*fac2[i-2]*i%mod;
    inv2[0]=inv2[1]=1;
    for(int i=2;i<=n;i++) inv2[i]=1ll*inv2[i-2]*iv[i]%mod;
}
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){x=(x-y<0?x-y+mod:x-y);}
int main()
{
    init();
    int n;scanf("%d",&n);
    int ans=n*2%mod;
    for(int d=0;d<n;d++)
    {
        int res=1ll*fac2[n*2-d-1]*(d?inv2[d-1]:1)%mod*inv2[n*2-d]%mod*fac2[d]%mod;
        res=1ll*res*(n*2-d)%mod*iv[n*2]%mod;
        dec(ans,2ll*res%mod);
    }
    printf("%d\n",ans);
    return 0;
}

D. Everyone is a winner

题解

神仙题。考虑这样一个贪心:先让所有人先取 3,再取 2,最后取 1,这样任意时刻前缀和都是最大的。

然后从后往前依次确定每个人。对于第 i 个人,假设当前最大值为 x,先让他尽可能接触 x,然后再尽可能地取 1。可以证明这样构造如果合法,那么总体就是合法的,并且可以证明超过 x 的部分不会非法。

容易发现斜率只有 1,2,3,直接记录两个值即可。复杂度 O(n)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010,inf=1e9;
int a[N],b[N],c[N];
int main()
{
    int T;scanf("%d",&T);
    while(T --> 0)
    {
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
        int f1=inf,f0=inf;
        for(int i=1;i<=n;i++) f0=min(f0,c[i]*2+b[i]),f1=min(f1,c[i]);
        bool can=true;
        for(int i=n;i;i--)
        {
            int mn=min(f0,min(f1+i,i*2)),l=max(0,i-b[i]-a[i]),r=min(c[i],min(i,mn/2));
            if(l>r || i-mn+l>a[i]){can=false;break;}
            int res=min(r,a[i]-i+mn);
            f0=min(f0,min(mn,min(res+i,res*2+b[i]))),f1=min(f1,res);
        }
        puts(can?"Yes":"No");
    }
    return 0;
}

E. More Peaks More Fun

题解

容易发现 n1 个“山顶”其实是上界。分析一下可以发现整个序列最多只能存在一处 pi<pi+1<pi+1 的位置,其余部分必须一上一下。

不妨假设 ai<bi,如果一个盒子内按先 ab 放置称为“顺序”,否则是“逆序”。

首先先考虑计算强制顺序放置的合法方案数:容易发现,如果一个序列是合法的,那么删除其中 b 最小的元素后仍然应该是合法的。所以按 b 从大往小插入,显然插入后 (i1,i) 处一定合法,而 (i,i+1) 处合法当且仅当 ai+1<bi。所以只需要对每个 i 统计满足 bj>bi,aj<bij 数量,记作 fi。那么答案就是 (fi+1)

考虑最终方案中允许有一处不作为“山顶”出现。容易发现一种合法方案完全翻转总是能得到另一种合法方案,所以不妨枚举中间位置 x,强制钦定左边顺序,右边逆序。

为了保证右侧不能翻转,此时要求 ax+1>bx,即形成 1243 状的山顶。考虑此时计算贡献,所有 by>bx+1 的方案数会额外 +1,所有 by<bx 的方案数会额外 1。按 b 从大到小插入,用树状数组维护所有方案即可。

复杂度 O(nlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fi first
#define se second
using namespace std;
const int N=400010,mod=1000000007;
pair<int,int>a[N];int n,f[N],iv[N],pre[N],suf[N];
int ksm(int a,int b=mod-2)
{
    int r=1;
    for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) r=1ll*r*a%mod;
    return r;
}
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){x=(x-y<0?x-y+mod:x-y);}
namespace fenwick{
    int a[N];
    void clear(){for(int i=1;i<=n*2;i++) a[i]=0;}
    void add(int x,int v){for(;x<=n*2;x+=x&-x) ::add(a[x],v);}
    int qry(int x){int v=0;for(;x;x-=x&-x) ::add(v,a[x]);return v;}
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].fi,&a[i].se);
        if(a[i].fi<a[i].se) swap(a[i].fi,a[i].se);
    }
    sort(a+1,a+n+1);
    for(int i=n;i;i--) f[i]=fenwick::qry(a[i].fi)+1,fenwick::add(a[i].se,1);
    fenwick::clear();
    int res=0,s=1;
    for(int i=1;i<=n;i++) s=1ll*s*f[i]%mod,iv[i]=ksm(f[i]);
    res=s;
    pre[1]=iv[1];for(int i=2;i<=n;i++) pre[i]=1ll*pre[i-1]*iv[i]%mod*(f[i-1]-1)%mod;
    suf[n]=iv[n];for(int i=n-1;i;i--) suf[i]=1ll*suf[i+1]*iv[i]%mod*(f[i+1]+1)%mod;
    for(int i=n;i;i--) add(res,1ll*fenwick::qry(2*n-a[i].fi+1)*pre[i]%mod*s%mod),fenwick::add(2*n-a[i].se+1,suf[i]);
    printf("%d\n",res);
    return 0;
}
posted @   Flying2018  阅读(27)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示