Codeforces Round #890 Div.2
题号:1856A~E2
A
题面:
给定一个正整数
和一个长度为 的序列 ,重复执行以下操作直至 序列单调不减:
, 。注意这个算一次操作。 求一共需要执行多少次操作。
多测,共
组数据。 对于所有数据,保证
, , 。
考虑原操作只会使得
read(n);for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
sort(b+1,b+n+1);int tag=0;
for(int i=n;i;--i){
if(a[i]!=b[i]){
tag=1;
cout<<b[i]<<"\n";break;
}
}
if(!tag)cout<<"0\n";
B
题面:
给定一个正整数
和一个长度为 的序列 ,问是否存在正整数序列 满足以下条件:
, 。
。 若存在,输出
YES
,否则输出NO
。多测,共
组数据。 对于所有数据,保证
, , 。
对于这种构造与原序列不同,我们显然是选择 调整法,在本题中微调就可以了。
考虑对于
read(n);for(int i=1;i<=n;i++)read(a[i]);
int k=0,d=0;
for(int i=1;i<=n;i++){
k+=(a[i]==1);d+=a[i]-1;
}
if(d>=k&&n!=1)cout<<"Yes\n";
else cout<<"No\n";
C
给定两个正整数
和 ,以及一个长度为 的序列 ,你可以进行不超过 次如下操作(以下两个步骤合称一次操作):
选择一个
满足 ,且 。 将
增加 。 求操作完成后,
的最大可以是多少。 多测,共
组数据。 对于所有数据,保证
, , 。
我们想想一个数是最大值满足什么条件,设最优局面为
面对此类构造性问题,我们可以从最优局面的角度出发,反推回原本局面寻找性质,类似于双向搜索。运用从后往前推的方法。
说明
所以这个最大值必然形成了一段单减序列,且相邻差为一。
那么假设我们已经知道了
由于最大操作次数是已知的,且答案显然具有单调性,故可对于每一个数来二分答案改为判定。我们只需计算上述代价即可。同时需要注意这个
bool check(int id,int x){
int s=k,tag=0;
for(int i=1;i<=n;i++)b[i]=a[i];
s-=x-b[id];b[id]=x;
for(int i=id+1;i<=n;++i){
if(b[i]<b[i-1]-1){
s-=(b[i-1]-1-b[i]);
b[i]=b[i-1]-1;
}
else {
tag=1;break;
}
}
return s>=0&&tag;
}
signed main(){
int t;read(t);
while(t--){
read(n);read(k);for(int i=1;i<=n;i++)read(a[i]);
int ans=0;
for(int i=1;i<=n;i++){
int l=a[i],r=k+a[i];
while(l<r){
int mid=l+r+1>>1;
if(check(i,mid))l=mid;
else r=mid-1;
}
ans=max(ans,l);
}
cout<<ans<<"\n";
}
}
D
本题交互。
有一个长为
的序列 ,最初不知道 的任何一个值。 一次询问区间
的逆序对个数,代价为 。 你需要进行若干次询问,求出
的最大值所在的位置,并使得询问总代价 。 多测,
。所有数据中 的总和不超过 。
对于限定区间代价的问题,我们先判断分开区间判断更优还是合并区间判断更优
看这个代价,对于长为
所以显然是询问小段更优,这启发我们自底向上确定最大值,设询问
我们要找最大值位置,怎么判断一个区间的最大值?设这个最大值在位置
考虑到我们以后必然是要合并若干区间的答案的,则我们对于区间
故可以分治解决该问题,代价?
考虑当
计算代价,对于
也即代价为
所以是一个可行的方案。
int ask(int l,int r){
if(l==r)return 0;
cout<<"? "<<l<<" "<<r<<"\n";cout.flush();
int x;cin>>x;return x;
}
int solve(int l,int r){
if(l==r)return l;
int mid=l+r>>1;
int x=solve(l,mid),y=solve(mid+1,r);
int a=ask(x,y),b=ask(x+1,y);
if(a-b==y-x)return x;
return y;
}
int main(){
ios::sync_with_stdio(false);
int t;cin>>t;
while(t--){
int n;cin>>n;int ans=solve(1,n);
cout<<"! "<<ans<<"\n";
}
}
事实上分治法在确定位置的问题上是常用的。但为什么我没想到呢?
确定一个最大值必然需要确定两段区间的最大值再比较谁更大啊。
E2
这是问题的困难版本,两个版本之间的唯一区别是
的范围和时间限制的不同。 给定一棵以
为根的有根树,你需要给出一个 到 的排列 ,最大化二元组 的数量,满足 ,输出这个最大值。
。
先考虑一个简化版问题,这很像二叉树的中序遍历。亦或者说是中序遍历版dfs序。
扩展一下,原问题就变为确定一个中序遍历的顺序,使满足要求的点对尽量多。
那么,假设进入子树
由于
则问题化为:给定
这是一个经典的动态规划求解可行性的问题,可以使用01背包在
对于每一颗子树做这样的动态规划,其复杂度是
这时候我们就可以通过E1的
先用一个启发式,若
在不久之前的一把CF里有一个去重后使用倍数法保障复杂度
重要性质:
推论:
的不同值最多只有 个,其中 为 中最小的 ,显然正实数根只有这一个,可以提前预处理亦或者二分求解。
的不同值最多只有 个,其中 为满足 中最小的 ,这可以直接定为 , 为一个自己设置的参数,一般二三四吧。
那么我们可以通过统计个数,去重做多重背包,用余数分组可以做到
二进制拆分的做法可以使用 bitset
优化,复杂度为
但问题是,即使不加bitset也可以在1902ms内跑过去。。。。。。
加bitset是一个实现难点,因为我们无法使用不定长bitset,此时就自己手写,亦或者把2的幂次的bitset全部定义一遍,一共20个,再写20个if判断即可。
void dfs(int u,int fa){
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
dfs(v,u);siz[u]+=siz[v];
}
siz[u]++;int mx=0;cnt=num=0;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
mx=max(mx,siz[v]);a[++cnt]=siz[v];
}
if(mx>=siz[u]/2){
ans+=(siz[u]-mx-1)*mx;return;
}
sort(a+1,a+cnt+1);
for(int i=1;i<=cnt;i++){
if(a[i]!=a[i-1])w[++num]=a[i],c[num]=0;
++c[num];
}
f[0]=1;
cnt=0;
for(int i=1;i<=num;i++){
int x=c[i];
for(int j=0;j>=0;++j){
if(x<(1<<j)){
a[++cnt]=x*w[i];break;
}
x-=(1<<j);
a[++cnt]=(1<<j)*w[i];
if(!x)break;
}
}
for(int i=1;i<=cnt;i++){
for(int j=siz[u]/2;j>=a[i];j--)f[j]|=f[j-a[i]];
}
int s=0;
for(int i=1;i<=siz[u]/2;++i)if(f[i]){
s=max(s,i*(siz[u]-i-1));f[i]=0;
}ans+=s;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!