当前位置: 首页 > java >正文

AtCoder Beginner Contest 408 D-F 题解

D. Flip to Gather

题意

给你一个长度为 N N N 01 01 01 字符串 S S S,每次操作可以选择 1 ≤ i ≤ N 1\le i\le N 1iN,把 S i S_i Si 变成 1 − S i 1-S_i 1Si

你要使得这个字符串的所有 1 1 1 必须连续存在,求最小操作数。

思路

假设最终的序列从 l l l r r r 都是 1 1 1,其余是 0 0 0;设 S S S 表示这个答案需要的操作数。

不难想到前缀和维护区间和,设 a i , b i a_i,b_i ai,bi 分别表示前 i i i 位中为 0 , 1 0,1 0,1 的数量。


S = [ l 左边 1 的数量 ] + [ l ∼ r 之间 0 的数量 ] + [ r 右边 1 的数量 ] = b l − 1 + ( a r − a l − 1 ) + ( b n − b r ) = ( b l − 1 − a l − 1 ) − ( b r − a r ) + b n \begin{align} S&=[l左边1的数量]+[l\sim r之间0的数量]+[r右边1的数量]\\ &=b_{l-1}+(a_r-a_{l-1})+(b_n-b_r) \\ &=(b_{l-1}-a_{l-1})-(b_r-a_r)+b_n \end{align} S=[l左边1的数量]+[lr之间0的数量]+[r右边1的数量]=bl1+(aral1)+(bnbr)=(bl1al1)(brar)+bn
由于 b n b_n bn 的值与 l , r l,r l,r 无关,所以可以把它提出来,最后再加上,所以,令 S ′ = S − b n S'=S-b_n S=Sbn

S ′ = ( b l − 1 − a l − 1 ) − ( b r − a r ) S'=(b_{l-1}-a_{l-1})-(b_r-a_r) S=(bl1al1)(brar),可以发现,对于每个 r r r,我们只要找到最小的 ( b l − 1 − a l − 1 ) (b_{l-1}-a_{l-1}) (bl1al1) 即可,在前面计算过程中用一个变量 m n mn mn 记录就可以了。

最终答案为 S ′ + b n S'+b_n S+bn

C++ 代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve(){int n;string s;cin>>n>>s;int ans=0,mn=0;int one=0,zero=0;//即上文说的b_i和a_ifor(int i=0;i<n;i++){if(s[i]=='1') one++;else zero++;ans=min(ans,mn-(one-zero));mn=min(mn,one-zero);}cout<<ans+one<<endl;
}
signed main(){int T;cin>>T;while(T--){solve();}return 0;
}

E. Minimum OR Path

题意

给你一个 N N N 个点 M M M 条边的无自环的无向图,第 i i i 条边连接 u i u_i ui v i v_i vi,权值为 w i w_i wi

在所有从 1 1 1 n n n 的简单路径(不经过同一点两次的路径)中,找到边权或和的最小值。

思路

贪心。把最终答案转换成二进制数看,接下来所说的高位、低位都指二进制位:

可以发现,越高位的贡献越大,而且二进制数 100000 > 011111 100000>011111 100000>011111,即,只要高位是 0 0 0,不管低位怎么增加都还是比原来小;

所以,要使答案最小,首先要使高位尽可能小。

那就可以从高位往低位贪心,假设一开始的答案的 31 31 31 位每一位都是 1 1 1。从第 31 31 31 位开始,假设这一位是 0 0 0,并把这个图的边全部删除(认为这个图上只有点,无边,后续逐渐加边),然后将所有的满足 [ 其边权的 ( 当前位 ) 和 ( 之前已经确定不是 1 的位 ) 不是 1 ] [ 其边权的(当前位)和(之前已经确定不是 1的位)不是 1] [其边权的(当前位)(之前已经确定不是1的位)不是1] 的边全部加入这个图,检测这个图是否连通。

若连通,说明这一位可以为 0 0 0,且这一位为 0 0 0 时的对答案的贡献一定比为 1 1 1 时的小,所以将答案的这一位设为 0 0 0

否则,说明这一位只能为 1 1 1,那么答案的这一位还是 1 1 1,保留原值。

接下来说一下如何判断图是否联通:使用并查集( DSU \text{DSU} DSU)。对于每一位,处理之前先把 f a i fa_i fai 初始化为 i i i,然后,对于每条满足要求(上面已经说过了)的边 ( u , v ) (u,v) (u,v),将 f a f i n d ( v ) = f a f i n d ( u ) fa_{find(v)}=fa_{find(u)} fafind(v)=fafind(u)

时间复杂度为 O ( ( N + M ) × log ⁡ N ) O((N+M)\times\log N) O((N+M)×logN)

C++ 代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=200005;
int n,m;
int fa[maxn];
struct Node{int u,v,w;
}e[maxn];
int find(int x){if(fa[x]==x) return fa[x];return fa[x]=find(fa[x]);
}
signed main(){cin>>n>>m;int one=0,zero=0;for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;e[i]={u,v,w};}for(int a=30;a>=0;a--){for(int i=1;i<=n;i++){fa[i]=i;}for(int i=1;i<=m;i++){if(e[i].w&(zero|(1<<a))) continue;int u=e[i].u,v=e[i].v;if(u>v) swap(u,v);if(find(u)==find(v)) continue;fa[find(v)]=fa[find(u)];}if(find(n)==find(1)){zero|=(1<<a);}else{one|=(1<<a);}}cout<<one<<endl;return 0;
}

F. Athletic

题意

N N N 个架子,第 i i i 个高度为 H i H_i Hi H H H 为长度为 N N N 的排列。一开始,你可以选择站在任意一个架子上,当你在架子 i i i 上时,你可以移动到满足以下条件的任意一个架子 j j j 上:

  • H i − H j ≥ D H_i-H_j \ge D HiHjD 1 ≤ ∣ i − j ∣ ≤ R 1\le |i-j|\le R 1ijR D D D R R R 已经给定)。

求出你的最大移动次数。

思路

考虑动态规划。设 f i f_i fi 表示到高度 i i i 的那个架子移动的最大次数, p o s i pos_i posi 表示 i i i H H H 中出现的位置。

则朴素的转移为 f i = max ⁡ [ 1 ≤ j ≤ N ] 且 [ p o s i − R ≤ p o s j ≤ p o s i + R ] 且 [ j − i ≥ D ] f j + 1 f_i=\max_{[1\le j\le N ]且[pos_i-R\le pos_j\le pos_i+R]且[j-i\ge D]}f_j+1 fi=max[1jN][posiRposjposi+R][jiD]fj+1

显然这个复杂度是 O ( N 2 ) O(N^2) O(N2) 的,所以我们要想出一个 O ( log ⁡ n ) O(\log n) O(logn) O ( 1 ) O(1) O(1) 查询区间最大值的方法。

考虑线段树维护区间最大值,对于每个高度 i i i,将 f i + D f_{i+D} fi+D 的值加入线段树(若 i + D > N i+D>N i+D>N 就不加),然后区间查询 [ p o s i − R , p o s i + R ] [pos_i-R,pos_i+R] [posiR,posi+R] 的最大值为 p p p,则 f i = p + 1 f_i=p+1 fi=p+1。最终最大的 f i f_i fi 值即为答案。

时间复杂度 O ( N log ⁡ N ) O(N\log N) O(NlogN)

C++ 代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=2e9;
const int maxn=500005;
struct SEGMENT_TREE{int mx[maxn<<2];void init(){memset(mx,-1,sizeof(mx));}void update(int x,int pos,int val,int l,int r){if(l==r) return void(mx[x]=val);int mid=l+r>>1;if(pos<=mid) update(x<<1,pos,val,l,mid);else update(x<<1|1,pos,val,mid+1,r);mx[x]=max(mx[x<<1],mx[x<<1|1]);}int query(int x,int L,int R,int l,int r){if(L<=l&&r<=R) return mx[x];int mid=l+r>>1,ans=-inf;if(L<=mid) ans=max(ans,query(x<<1,L,R,l,mid));if(R>mid) ans=max(ans,query(x<<1|1,L,R,mid+1,r));return ans;}
}TR;
int h[maxn],f[maxn];
int pos[maxn];
signed main(){int n,D,R;cin>>n>>D>>R;TR.init();for(int i=1;i<=n;i++){cin>>h[i];pos[h[i]]=i;}for(int i=n;i>=1;i--){if(i+D<=n) TR.update(1,pos[i+D],f[i+D],1,n);f[i]=TR.query(1,max(1ll,pos[i]-R),min(n,pos[i]+R),1,n)+1;}int ans=0;for(int i=1;i<=n;i++){ans=max(ans,f[i]);}cout<<ans<<endl;return 0;
}
http://www.xdnf.cn/news/12694.html

相关文章:

  • JDK8安装与配置
  • 探索Python融合地学:斗之气七段(运算符)
  • 冰箱智能化升级方案:WT3000A离在线AI语音模组赋能AI在线对话功能
  • Cline核心说明文档
  • 基于Java的离散数学题库系统设计与实现:附完整源码与论文
  • mysql整体架构
  • 在 Windows 11 或 10 上将 Visual Studio Code 添加到系统路径
  • C++学习-入门到精通【15】异常处理深入剖析
  • (附实例代码及图示)混合策略实现 doc-doc 对称检索
  • FreeRTOS任务调度过程vTaskStartScheduler()任务设计和划分
  • redis分布式锁
  • Python训练营打卡DAY47
  • 4G物联网模块提升智慧农业的自动化生产效率
  • 【CSS-5】深入理解CSS复合选择器:提升样式表的精确性与效率
  • 第三章支线二 ·函数幻阶:语法召唤与逻辑封印
  • A Execllent Software Project Review and Solutions
  • 函数式接口实现分页查询
  • AI开发 | 生成式AI在企业软件中的演进形态:从嵌入式到智能体
  • nodejs:用 nodemailer 发送一封带有附件的邮件
  • 【JavaSE】集合学习笔记
  • C++ 对 C 的兼容性
  • 基于Scala实现Flink的三种基本时间窗口操作
  • 跨平台资源下载工具:res-downloader 的使用体验
  • Vue3中computed和watch的区别
  • OpenLayers 导航之运动轨迹
  • 深入剖析 RocketMQ 中的 DefaultMQPushConsumerImpl:消息推送消费的核心实现
  • Docker基础(二)
  • TTL简述
  • Unity基础-欧拉角和四元数
  • 【Elasticsearch】映射:Join 类型、Flattened 类型、多表关联设计