CF round1005(div2)总结
ps:本人被D题硬控1个半小时直接洪文,半夜掉大分,此博客是在极佳快死了的状态下写出来的,有一些类似人类的语言自行理解
更新:12.17添加E题解
链接:A
首先,我们可以将所有连续的1合并为1个,因为这些1都可以在同一次操作中去除
然后,我们考虑,对于不在末尾的1,因为我们要保证原序列只剩0,所以我们需要两次操作,先把1和后面都移过去,在把0移回来
而对于末尾的1,直接移过去就好了
所以我们直接统计1的个数*2再特判一下就好了
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1010
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll T,n;
char c[MAXN];
void work(){
n=read();
cin>>(c+1);
ll ans=0;
for(int i=1;i<=n;i++){
if(c[i]=='1'&&c[i+1]!='1'){
ans+=1+(i<n);
}
}
cout<<ans<<endl;
return;
}
int main(){
T=read();
while(T--)work();
return 0;
}
链接:B
把得分转化一下,一个序列的得分就是序列里每个前面已经出现过相同数的数的个数
所以,我们可以发现,删去其中一部分后,序列得分只降不增
那其实我们的问题就转化成了找一个最大的区间,这个区间中的所有数都只出现过一次,删去这些数不会对得分产生任何影响
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200010
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll T,n;
ll a[MAXN],pre[MAXN];
map <ll,ll> mp;
void work(){
n=read();
ll ans=0;
for(int i=1;i<=n;i++){
a[i]=read();
mp[a[i]]++;
}
for(int i=1;i<=n;i++){
if(mp[a[i]]>1){
pre[i]=0;
}else pre[i]=pre[i-1]+1;
if(pre[i]>pre[ans]){
ans=i;
}
}
for(int i=1;i<=n;i++)mp[a[i]]=0;
if(ans==0)cout<<"0\n";
else cout<<ans-pre[ans]+1<<" "<<ans<<endl;
return;
}
int main(){
T=read();
while(T--)work();
return 0;
}
链接:C
根据取正数和取负数分别删除前缀后缀的特性,我们可以构造贪心的解法,那就是从两边往中间选
具体来说,我们指定一个位置x最后选,先从1到x选正数,再从n到x选负数
这样的话我们处理一下前缀和后缀和,然后枚举位置x,取最大值即可
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200010
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll T,n;
ll a[MAXN],pre[MAXN],suf[MAXN];
void work(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
pre[0]=suf[n+1]=0;
for(int i=1;i<=n;i++){
pre[i]=pre[i-1];
if(a[i]>0)pre[i]+=a[i];
}
for(int i=n;i;i--){
suf[i]=suf[i+1];
if(a[i]<0)suf[i]-=a[i];
}
ll ans=0;
for(int i=1;i<=n;i++)ans=max(ans,suf[i]+pre[i]);
cout<<ans<<endl;
return;
}
int main(){
T=read();
while(T--)work();
return 0;
}
链接:D
傻逼fhx多测没清空本地找了一个小时,找到以后有因为逆天小细节吃了6发罚时,最后D题喜提674超高分
首先,看到异或运算,我们想到先拆位
然后,我们发现,这是一道比大小的题目,我们从最高位往最低位考虑
假如我们考虑完前面几位后已经吃掉了任意个粘液,那么最多再吃掉一个当前位为1的数,因为吃掉以后当前位就变成0了
所以我们从最高位考虑到最低位,记录已经吃掉了几个粘液,如果吃掉这些粘液后当前位仍然为1,那么就可以继续往前吃一个,从后往前找到原序列中第一个当前位为1的数,把他后面的都吃掉
所以我们考虑预处理一个pre数组,
但是,我们发现一个严重的问题,那就是二进制下所有位置的限制是同时生效的,也就是假如在i位吃完以后我们后面只能吃第i位为0的数了
我们可以记录一个maxz,表示前面所有位中对答案限制的最大值,我们吃到maxz就不能往下吃了
最后就是一些小细节了,关于maxz的处理和往前吃的过程可以看ACcode
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200010
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll pre[MAXN][50],suf[MAXN][50],mp[50];
ll a[MAXN],n,q;
void work(){
n=read(),q=read();
for(int i=0;i<=30;i++)mp[i]=0,suf[n+1][i]=0,pre[0][i]=0;
for(int i=1;i<=n;i++){
a[i]=read();
for(int j=0;j<=30;j++){
if(a[i]&(ll)(1ll<<j))mp[j]=i;
pre[i][j]=mp[j];
}
}
for(int i=n;i;i--){
for(int j=0;j<=30;j++){
suf[i][j]=suf[i+1][j];
if(a[i]&(ll)(1ll<<j))suf[i][j]^=1;
}
}
for(int i=1;i<=q;i++){
ll qs=read(),now=n,maxz=0;
for(int j=30;j>=0;j--){
ll p=(qs&(ll)(1ll<<j))?1:0;
p^=suf[now+1][j];
if(p>0){
now=pre[now][j];
if(now<=maxz)break;
maxz=max(maxz,pre[now-1][j]);
}else if(a[now]&(ll)(1ll<<j))break;
else{
maxz=max(maxz,pre[now][j]);
}
if(!j)now--;
}
cout<<n-max(maxz,now)<<" ";
}
cout<<endl;
}
int main(){
ll T=read();
while(T--)work();
return 0;
}
链接:E
首先,我们可以知道,每一行的颜色是不能更换的,因为第一列不会下落,如果我们更换了每一行的颜色,那么第一列一定不符合要求
其次,什么时候两行交换位置不会对最终的形态造成影响,当我们找到颜色相同的两行i,j且i,j中间所有不同颜色行的长度都小于i,j
这个时候,i,j两行的沙块会在最终的三角形中相接,可以自己手模一边
这样的话,一行对于答案的影响是所有满足上述条件的行的个数,讲所有行的贡献相乘就是答案
我们可以从长度小的行开始,统计完长度为i的行后把这一行删去,这样,当我们遍历到一行i时,所有与i满足条件的行j都会与i联通(中间颜色都相同
我们可以用链表维护删边,然后用并查集维护连通块大小,删除一行后判断两端颜色是否相同,相同的话就将并查集合并,这一行对答案的贡献也就等于连通块大小
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 998244353
#define MAXN 200010
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
struct node_dsu{
ll fa[MAXN],siz[MAXN],col[MAXN];
void getin(ll id){
fa[id]=id,siz[id]=1,col[id]=read();
}
ll get(ll x){
if(fa[x]==x)return x;
else return fa[x]=get(fa[x]);
}
void merge(ll x,ll y){
x=get(x),y=get(y);
if(col[x]!=col[y]||x==y)return;
fa[x]=y;
siz[y]+=siz[x];
}
}dsu;
struct node_chain{
ll pre[MAXN],nxt[MAXN];
void build(ll x){
for(int i=2;i<x;i++){
pre[i]=i-1,nxt[i]=i+1;
}
pre[1]=-1,nxt[1]=2;
pre[x]=x-1,nxt[x]=-1;
}
void erase(ll pos){
nxt[pre[pos]]=nxt[pos];
pre[nxt[pos]]=pre[pos];
dsu.siz[dsu.get(pos)]--;
if(nxt[pos]!=-1&&pre[pos]!=-1)dsu.merge(pre[pos],nxt[pos]);
}
}chain;
ll n,T,ans;
ll pos[MAXN];
void work(){
ans=1;
n=read();
for(int i=1;i<=n;i++){
ll p=read();
pos[p]=i;
}
for(int i=1;i<=n;i++){
dsu.getin(i);
dsu.merge(i,i-1);
}
chain.build(n);
for(int i=1;i<=n;i++){
// cout<<pos[i]<<" "<<dsu.get(pos[i])<<" "<<dsu.siz[dsu.get(pos[i])]<<endl;
ans=ans*dsu.siz[dsu.get(pos[i])]%mod;
chain.erase(pos[i]);
}
cout<<ans<<endl;
return;
}
int main(){
T=read();
while(T--)work();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话