AtCoder Grand Contest 053
链接
B. Taking the middle
题解
首先可以把序列按 划开,第一个序列从 到 ,第二个序列从 到 。可以发现,每次操作中对手取出较多的那个序列的最开头元素。
容易证明,一组选择是合法的当且仅当对于任意 ,两个序列的前 个数字中至少有 个被对手选取了。这样直接从前往后带悔贪心即可。每次让对手选出当前最小的没有被选的元素即可。
复杂度 。
代码
#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
题解
首先 所在的堆一定会被保留下来,所以相当于要用最小的步数移走另一堆卡片。考虑左边的每张卡片,它需要匹配右边的一张比他大卡片。容易证明,答案是左边每张卡片位置(从上往下)减去右边比他大的第一张卡片位置之差的最大值。
枚举这个值 ,考虑计算答案 的方案数。直接计算有点麻烦,但是容易发现一点是,如果一张卡片的上方有一张比他大的卡片,那么这张卡片一定不会成为限制。所以从上往下考虑卡片 ,要求就是当前牌堆 和另一边牌堆 中当前位置 不是最大的。
由于要求之间相互独立,所以答案是每个位置答案相乘。直接预处理双阶乘及其逆元即可。复杂度 。
代码
#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
题解
神仙题。考虑这样一个贪心:先让所有人先取 ,再取 ,最后取 ,这样任意时刻前缀和都是最大的。
然后从后往前依次确定每个人。对于第 个人,假设当前最大值为 ,先让他尽可能接触 ,然后再尽可能地取 。可以证明这样构造如果合法,那么总体就是合法的,并且可以证明超过 的部分不会非法。
容易发现斜率只有 ,直接记录两个值即可。复杂度 。
代码
#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
题解
容易发现 个“山顶”其实是上界。分析一下可以发现整个序列最多只能存在一处 的位置,其余部分必须一上一下。
不妨假设 ,如果一个盒子内按先 后 放置称为“顺序”,否则是“逆序”。
首先先考虑计算强制顺序放置的合法方案数:容易发现,如果一个序列是合法的,那么删除其中 最小的元素后仍然应该是合法的。所以按 从大往小插入,显然插入后 处一定合法,而 处合法当且仅当 。所以只需要对每个 统计满足 的 数量,记作 。那么答案就是 。
考虑最终方案中允许有一处不作为“山顶”出现。容易发现一种合法方案完全翻转总是能得到另一种合法方案,所以不妨枚举中间位置 ,强制钦定左边顺序,右边逆序。
为了保证右侧不能翻转,此时要求 ,即形成 状的山顶。考虑此时计算贡献,所有 的方案数会额外 ,所有 的方案数会额外 。按 从大到小插入,用树状数组维护所有方案即可。
复杂度 。
代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理