Codeforces Round 893 Div.2 A~E2
Codeforces Round 893 Div.2 A~E2
CF1858A
有
个球。
其中的个只能被先手取, 个只能被后手取, 个双方都可以取。 双方轮流取球,每次取走一个,无法取球者输。
组询问,每次给定 问哪一方必胜。
考虑到只需要关心谁先不能取即可。
显然能取必定是先取那
CF1858B
你背着一个有无穷多饼干的书包去逛街。
街上从左到右一共有
个摊位和 个商人,第 个商人在第 个摊位卖饼干。 你从最左边开始,向右依次走过第
个摊位,从一个摊位走到和它相邻的摊位需要 分钟。 每当走过第
个摊位时,你会依照这样的条件吃饼干:
- 如果这个摊位上有一个商人,则从商人那里购买一个饼干并立即吃掉。
- 否则如果你还没有吃过饼干,则从书包里拿出一个饼干并立即吃掉。
- 否则如果你前一次吃饼干到现在已经过了
分钟,也就是你在第 个摊位都没有吃饼干,则从书包里拿出一个饼干并立即吃掉。 注意这些条件不会同时满足,亦即你在同一个摊位不会吃掉超过
个饼干。 如今人员拥挤,管理部门需要移除恰好一个商人。
你想知道怎样移除一个商人使你能吃到的饼干最少。
请输出移除一个商人后你能吃到的最少饼干数量,以及有多少种移除方案使你能吃的饼干最少。
, , , 单调递增, 。
注意到原则: 简化边界判断
为了满足在第一个位置吃东西,可以建立
然后设
则在不删人的情况下,
那么考虑删掉
显然,
注意若这个个数为0,则满足条件的个数为
signed main(){
int t=read();
while(t--){
int m,d;k=0;n=read(),m=read(),d=read();s[++k]=-d+1;
for(int i=1;i<=m;i++)s[++k]=read();int tag=0,ans=0;
if(d==1){
cout<<n<<" "<<m<<"\n";continue;
}
s[++k]=n+1;
for(int i=1;i<k;i++)w[i]=(s[i+1]-s[i]-1)/d,ans+=w[i];ans+=m;
for(int i=2;i<k;i++){
if((s[i+1]-s[i-1]-1)/d<w[i]+w[i-1]+1)++tag;
}
if(tag)cout<<ans-1ll<<" "<<tag<<"\n";
else cout<<ans<<" "<<m<<"\n";
}
}
CF1858C
要求构造一个
的排列,编号为 ,使得 最大。 n=5: 1 2 4 3 5
n=7: 1 2 3 6 4 5 7
n=10: 1 2 3 4 8 5 10 6 9 7
观察样例,不难发现令
那么我们倍增去构造这些值即可。这是必定可以构造出来的。(想一想,为什么)
void get(int x){
if(v[x]||x>n)return ;
b[++num]=x,v[x]=1;
get(x<<1);
}
//main
cin>>n;num=0;
for(int i=1;i<=n;i++){
get(i);
}
for(int i=1;i<=n;i++)if(!v[i])b[++num]=i;
for(int i=1;i<=n;i++)v[i]=0;
for(int i=1;i<=n;i++)cout<<b[i]<<" ";
cout<<"\n";
CF1858D
给出一个01串,可以进行不超过
次将一个位取反的操作。 设
为更改后最长的0段长度,设 为更改后最长的1段长度。 对于每一个
,求 的最大值。
简单问题。其实拆一下变成
不妨设
则
考虑求出
可以计算出在
这两边是不同的两个同类子问题,可以通过动态规划求得答案。
设
则分为两种情况:最长
对于不是
对于是
两种情况取较大值即可。
最后本要做一个后缀最小值,但做不做无所谓了,等价的。
cin>>n>>m;
for(int i=1;i<=n;i++){
char x;cin>>x;a[i]=x-'0';
}
for(int i=0;i<=n;i++)f[i]=ans[i]=-inf;
for(int i=0;i<=n+1;i++)for(int j=0;j<=n;j++)pre[i][j]=suf[i][j]=0;
for(int i=1;i<=n;i++){
int c=0;
for(int j=0;j<=n;j++)pre[i][j]=pre[i-1][j];
for(int j=i;j;--j){
c+=(!a[j]);
pre[i][c]=max(pre[i][c],i-j+1);
}
}
f[0]=pre[n][m];
for(int i=n;i;--i){
int c=0;
for(int j=0;j<=n;j++)suf[i][j]=suf[i+1][j];
for(int j=i;j<=n;j++){
c+=(!a[j]);
suf[i][c]=max(suf[i][c],j-i+1);
}
}
for(int i=1;i<=n;i++){
int c=0;
for(int j=i;j<=n;j++){
c+=a[j];if(c>m)break;
f[j-i+1]=max(f[j-i+1],max(pre[i-1][m-c],suf[j+1][m-c]));
}
}
for(int i=1;i<=n;i++)for(int j=0;j<=n;j++)ans[i]=max(ans[i],i*j+f[j]);
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";cout<<"\n";
CF1858E2
维护一个初始为空的序列,支持以下操作:
:在序列末端插入 ; :在序列末端删除 个数( 不超过当前序列长度); :查询序列中不同的数字个数; :撤回前一个 操作。
次操作,强制在线, ,询问操作不超过 。
操作问题,可以用操作树来考虑,显然真正有意义的操作只是插入。
我们考虑记录节点
则对于操作1,直接新开一个儿子
对于操作2,直接将
对于操作4,我们可以维护上一次进行1/2操作的指针位置,并回跳后更新当前操作即可
对于询问答案,直接在插入时就维护
考虑维护。
可以这样抽象一颗操作树,将儿子分为两类。第一类是表示操作1出来的,第二类是表示其他操作的。
其他操作的点是没有子树的。
然后对于2,3,4的更新,直接找到前一个对应位置,将其在树上所对一类点作为自己的父亲,顺带转移信息。
我们只需要考虑操作1,2怎么实现。
操作2很简单,做一个树上
操作1?我们可以使用主席树维护所有的一类点。
时间复杂度
这里我们就可以注意到,在主席计算时,无需记录原本区间的左右端点,边传边算。
同时注意我们只是查一个数值是否为1,所以只需要看能否走到对应位置(是否存在这个点)即可。
总的来说,只需要记录动态开点的左右儿子即可。
空间255MB,emmmm。
维护四个东西:
:上一个有效操作所对位置 :实际指向的一类点位置 :目前答案 :倍增 级祖先
对于几个操作状态的维护,就直接看代码了,这里不详细叙述了。
#include<iostream>
#include<cstdio>
using namespace std;
int cnt;
struct node {
int lc, rc;
}t[21000005];
void update(int &s,int L,int R,int pos,int dis) {
s=++cnt;int mid=L+R>>1;
t[s]=t[pos];
if (L==dis&&R==dis)return;
if (dis>=L&&dis<=mid)update(t[s].lc,L,mid,t[pos].lc,dis);
else update(t[s].rc,mid+1,R,t[pos].rc,dis);
}
int find(int pos,int L,int R, int dis) {
int s=pos;
if(!s)return 0;
if(L==R)return 1;int mid=L+R>>1;
if(dis<=mid)return find(t[s].lc,L,mid, dis);
else return find(t[s].rc,mid+1,R, dis);
}
int id,lst[1050000],len,c;
int f[1000005][20],s[1050000],ver[1050000];
void add(int x,int pos){
lst[pos]=pos-1;
ver[pos]=ver[pos-1];
s[pos]=s[pos-1];f[pos][0]=pos-1;
for(int i=1;i<20;i++)f[pos][i]=f[f[pos][i-1]][i-1];
if(find(ver[pos],1,1e6,x))return ;
update(ver[pos],1,1e6,ver[pos],x);
s[pos]++;
}
void del(int k,int pos){
lst[pos]=pos-1;
int id=pos-1;
for(int i=0;i<20;i++)if((k>>i)&1)id=f[id][i];
ver[pos]=ver[id],s[pos]=s[id];
for(int i=0;i<20;++i)f[pos][i]=f[id][i];
}
void move(int pos){
int id=lst[pos-1];
lst[pos]=lst[id];ver[pos]=ver[id],s[pos]=s[id];
for(int i=0;i<20;i++)f[pos][i]=f[id][i];
}
int solve(int pos){
lst[pos]=lst[pos-1],ver[pos]=ver[pos-1],s[pos]=s[pos-1];
for(int i=0;i<20;i++)f[pos][i]=f[pos-1][i];
return s[pos];
}
char o;
int main() {
int t,pos=0;
ios::sync_with_stdio(false);
cin >> t;
while(t--){
cin>>o;++pos;
if(o=='+'){
int k;cin>>k;
add(k,pos);
}
else if(o=='-'){
int k;cin>>k;
del(k,pos);
}
else if(o=='!'){
move(pos);
}
else {cout<<solve(pos)<<"\n";cout.flush();}
}
return 0;
}//codeforces
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!