AtCoder Grand Contest 011
AtCoder Grand Contest 011
upd:这篇咕了好久,前面几题是三周以前写的。。。
AtCoder Grand Contest 011
A - Airport Bus
翻译
有nn个乘客到达了飞机场,现在他们都要坐车离开机场。第ii个乘客到达的时间是TiTi,一个乘客必须在[Ti,Ti+k][Ti,Ti+k]时刻做到车,否则他会生气。一辆车最多可以坐CC个人。问最少安排几辆车可以让所有人都不生气。
题解
从前往后贪心即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 100100
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,C,K,T[MAX],ans,s,t;
int main()
{
n=read();C=read();K=read();
for(int i=1;i<=n;++i)T[i]=read();
sort(&T[1],&T[n+1]);
for(int i=1;i<=n;++i)
if(!s)++ans,s=1,t=T[i];
else
{
if(s==C)++ans,s=1,t=T[i];
else
{
if(T[i]-t>K)++ans,s=1,t=T[i];
else ++s;
}
}
printf("%d\n",ans);
return 0;
}
B - Colorful Creatures
翻译
题解
显然不合法的在排序之后是一段前缀,那么倒推哪些合法即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 100100
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,ans=1;ll a[MAX],s;
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
sort(&a[1],&a[n+1]);
for(int i=1;i<=n;++i)a[i]+=a[i-1];
for(int i=n-1;i;--i)
if(a[i]*2>=a[i+1]-a[i])++ans;
else break;
printf("%d\n",ans);
return 0;
}
C - Squared Graph
翻译
给定一个nn个点mm条边的图,构建一个n2n2个点的图,新图的每个点都可以看成一个二元组,新图上的点(a,b)(a,b)和(a′,b′)之间有边,当且仅当原图中(a,a′),(b,b′)之间有边,问新图的联通块个数。
题解
不太会,抄了一遍代码,从这里抄的
大概理解一下就是,首先考虑所有二元组很不好考虑,拆分行列,考虑情况。首先把所有没有连上的单点全部拿出来把贡献直接算好,接下来只会考虑所有存在边的联通块。考虑一下如果两个点(a,b),(x,y)在新图中连在一起,那么必定在原图中存在一条a→x,b→y的路径,并且满足两条路径的长度相同。
如果一个联通块是一个二分图,那么它自身就会贡献2的答案,即把二分图黑白染色之后左右分开,显然把两边的点分别放在二元组的前面都会形成一个联通块;否则会贡献1的答案。然后考虑任选两个不连通的图,他们之间的边的关系都可以在新图之中构成一个新的联通块,所以把贡献加上就好了。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<2];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
bool fl;int sum;
int dep[MAX];bool vis[MAX];
int n,m;ll ans;
void dfs(int u,int ff)
{
vis[u]=true;++sum;
for(int i=h[u];i;i=e[i].next)
if(vis[e[i].v]&&e[i].v!=ff)fl|=dep[u]==dep[e[i].v];
else if(!vis[e[i].v])dep[e[i].v]=dep[u]^1,dfs(e[i].v,u);
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
int node=0,t1=0,t2=0;
for(int i=1;i<=n;++i)
{
if(vis[i])continue;
fl=false;sum=0;dfs(i,0);
if(sum==1)ans+=n*2-1,++node;
else if(!fl)ans+=2,++t1;
else ++ans,++t2;
}
ans-=1ll*(node-1)*node;
ans+=1ll*t1*(t1-1)*2+1ll*t2*(t2-1)+1ll*t1*t2*2;
cout<<ans<<endl;
return 0;
}
D - Half Reflector
翻译
有一个神仙机器,你可以从它的左边或者右边丢一个球进去,它有两种状态A,B。如果处在A状态,你从哪里丢进来的就会从哪里吐出去,然后立即变成B状态。如果处在B状态,你从哪边丢进去就会从另外一边吐出来,然后立即变成A状态。现在把n台这样的机器放在一排,告诉你他们处在什么状态。然后从最左边放K次球,每当前面那个球出去之后就放第二个。问最后的机器的状态是啥。
题解
先想想一个球什么时候会从左边出来,什么时候从右边出来。如果它从左边出来,只可能是第一个机器处于A状态。我们来感性理解一下。首先加入你到达了中间某个位置i,并且它在这里被弹了回来,因为你到了i,所以i−1原先一定是B状态,所以它现在一定是A状态,那么你又被接着弹回去了,那么你又会回到i,而i刚刚是A,所以现在是B,所以会走到i+1,然后重复此个过程,它一定会向右走。
考虑一下这个串怎么变化。如果最高位是A,直接变成B完事。否则的话,我们发现首先把它们全部取反,然后原来A之前的那个B没有变反,原来B前面的那个A也没有变反。所以这个操作可以看成先把它左移一位(高位截掉),再全部取反,最后一个位置显然是补上一个A。发现做完最多2n次之后就和原来的串无关了,就变成了ABABAB...或者BABABA...这样子了。那就暴力做个2n次,剩下的判断一下就好啦,当然这里情况可能有些复杂,仔细手玩一下再写代码。
蛤?怎么模拟?你把数组在后面延长不就好了吗?
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
int n,K,l,r,p;
char S[MAX];int a[MAX<<2];
int main()
{
scanf("%d%d",&n,&K);scanf("%s",S+1);
l=1,r=n,p=0;
for(int i=1;i<=n;++i)a[i]=S[i]=='A';
for(int i=1;i<=n+n;++i)
{
if(a[l]^p)a[l]^=1;
else ++l,p^=1,a[++r]=p^1;
if(i==K)
{
for(int j=l;j<=r;++j)putchar((a[j]^p^1)+'A');
puts("");return 0;
}
}
if((n&1)&&(K&1))a[l]^=1;
for(int i=l;i<=r;++i)putchar((a[i]^p^1)+'A');
return 0;
}
E - Increasing Numbers
翻译
给定一个数n,n≤10500,000,问n最少可以拆分成几个不降数的和。一个不降数是在十进制位下,从高位往低位看,每个数都不会比高位的数更小的数。
题解
找啊找啊找性质。然而我太菜了,找不出性质。
首先意识不降的数一定能够拆分成若干个11....11的形式,因为数字不降,所以这个个数不会超过9个。那么按照这个方向考虑,假设最终的答案中我们的所有数字被我们拆成了若干个1...1的形式,他们分别有ai个1。那么等式算一下就是∑9ki=1(10ai−1−1)=9n。转化一下就是∑9ki=110ai=9n+9k,也就是9n+9k的所有位置上的数字之后恰好小于等于9k。那就直接大力枚举k就好了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 500500
char ch[MAX];
int a[MAX<<1],n,s;
int main()
{
scanf("%s",ch+1);n=strlen(ch+1);
for(int i=n;i;--i)a[i]=(ch[n-i+1]-48)*9;
for(int i=1;i<=n;++i)a[i+1]+=a[i]/10,a[i]%=10;
if(a[n+1])++n;
for(int i=1;i<=n;++i)s+=a[i];
for(int i=1;i<=n*9;++i)
{
a[1]+=9;int p=1;s+=9;
while(a[p]>=10)
{
s-=a[p+1];a[p+1]+=a[p]/10;s+=a[p+1];
s-=a[p];a[p]%=10;s+=a[p];
++p;if(p>n)n=p;
}
if(s<=i*9){printf("%d\n",i);return 0;}
}
return 0;
}
F - Train Service Planning
翻译
有n段铁路,n+1个车站,第i段铁路连接着站台i−1和站台i。第i段铁路行驶时间为Ai,铁路有两种,有一条路或者两条路,即一次可以开过一辆火车或者两辆火车。现在从0到n和从n到0每经过K个单位时间就会发出一辆车,一辆车可以在一个站台停任意久的时间,显然不能被后面的同向的车给追上,现在需要安排一个车的行驶方式,使得两侧运行的车的总运行时间最小。
题解
首先每隔K个单位时间就会发出一辆新车,意味着所有的运算可以等效的理解为在模K意义下进行。我们假设我们知道两辆车在每一个位置停下的时间,从0→n的设为t1[i],反过来的设为t2[i],那么我们就可以得到一辆车通过一段路的时间的区间。设S为前缀和。从0→n的车的通过第i段路的时间是[S(A,i−1)+S(t1,i−1),S(A,i)+S(t1,i−1)],反过来运行的车的通过时间同理,我们可以用后缀和表示出来,然后后缀又可以改写成总和减去一段前缀,也就是[Sum−S(A,i)−S(t2,i−1),Sum−S(A,i−1)−S(t2,i−1)]。而总和的话,我们可以强制认为后面从后往前的车在0车站还要多停一会儿,也就是强制让sum是K的倍数,那么在模K的意义下就可以直接把Sum省略掉。
对于双向轨道而言,我们并没有任何限制,对于单向轨道而言,显然强制两车通过同一段路的时间不能交。那么可以得到一系列的不等式。最终得到的也就是形如S(t1,i)+S(t2,i)∉[−2S(A,i),−2S(A,i−1)]。当然,这些东西全部都在模意义下看。也因为在模意义下,所以不属于可以取补化为属于,即S(t1,i)+S(t2,i)要在某个特定区间内。而题目要求的东西就是要最小化S(t1,n)+S(t2,n)。而问题也同时变为,给定n个区间,每次都要向前走若干距离,并且第i步走完后位置必须在模意义下的给定区间中,求最小的移动距离。
因为可以在起始车站停留,所以我们可以任意选择起始位置,贪心的考虑,显然每次走到目标区间的左端点一定是最优的。那么我们只需要预处理从每一个位置出发一直走左端点的最小距离即可。设f[i]表示当前从第i个限制的左端点出发的距离。考虑倒推,每次更新完一个区间之后就可以用线段树更新取值,这样子就很容易知道下一个位置要从哪里转移。接下来只需要枚举起点即可计算答案。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 200200
#define lson (now<<1)
#define rson (now<<1|1)
void WA(){puts("-1");exit(0);}
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,K,top;
ll a[MAX],b[MAX],t[MAX<<2];
ll L[MAX],R[MAX],f[MAX],s[MAX],S[MAX];
void pushdown(int now)
{
if(!t[now])return;
t[lson]=t[rson]=t[now];
t[now]=0;
}
void modify(int now,int l,int r,int L,int R,int w)
{
if(L>R)return;
if(L<=l&&r<=R){t[now]=w;return;}
int mid=(l+r)>>1;pushdown(now);
if(L<=mid)modify(lson,l,mid,L,R,w);
if(R>mid)modify(rson,mid+1,r,L,R,w);
}
int Query(int now,int l,int r,int p)
{
if(l==r)return t[now];
int mid=(l+r)>>1;pushdown(now);
if(p<=mid)return Query(lson,l,mid,p);
else return Query(rson,mid+1,r,p);
}
ll Query(int x)
{
int d=Query(1,1,top,x);
if(!d)return 0;
return f[d]+(S[L[d]]%K-S[x]%K+K)%K;
}
int main()
{
n=read();K=read();
for(int i=1;i<=n;++i)
{
a[i]=read(),b[i]=read();
s[i]=s[i-1]+a[i];
if(b[i]==1&&a[i]+a[i]>K)WA();
}
for(int i=n;i;--i)
if(b[i]==1)L[i]=(-2*s[i-1]%K+K)%K,R[i]=(-2*s[i]%K+K)%K;
else L[i]=0,R[i]=K-1;//No limit
for(int i=1;i<=n;++i)S[++top]=L[i],S[++top]=R[i];
sort(&S[1],&S[top+1]);top=unique(&S[1],&S[top+1])-S-1;
for(int i=n;i;--i)
{
L[i]=lower_bound(&S[1],&S[top+1],L[i])-S;
R[i]=lower_bound(&S[1],&S[top+1],R[i])-S;
f[i]=Query(L[i]);
if(L[i]>R[i])modify(1,1,top,R[i]+1,L[i]-1,i);
else modify(1,1,top,1,L[i]-1,i),modify(1,1,top,R[i]+1,top,i);
}
ll ans=1e18;
for(int i=top;i;--i)ans=min(ans,Query(i));
cout<<ans+s[n]+s[n]<<endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理