2021.7.13 提高B组模拟赛

2023-10-08 08:30
文章标签 模拟 13 提高 2021.7

本文主要是介绍2021.7.13 提高B组模拟赛,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2021.7.13 2021.7.13 2021.7.13 模拟赛 Ⅱ Ⅱ
目录:

T1.消息传递
T2.JIH的玩偶
T3.摘取作物
T4.公路维护

T 1 : T1: T1消息传递

在这里插入图片描述
在这里插入图片描述

分析:

洛谷有一道这题的弱 10 ^{10} 10化版 l i n k link link
这数据范围: n < = 200000 n<=200000 n<=200000
先是树形 d p dp dp f i f_i fi表示处理以 i i i为根的子树最少时间
从大到小排序后 f i f_i fi可以直接求出 f i = m a x ( f i , a i + i ) f_i=max(f_i,a_i+i) fi=max(fi,ai+i) 把这个交到洛谷就过了
再考虑换根 d p dp dp
g i g_i gi表示除 i i i这棵子树后 其他部分以 i i i的父亲为根的整棵树的答案
g i g_i gi已经求出 应该从通过 i i i求出每个儿子的 g g g

将所有的儿子和自己的 g g g值排序后 考虑删掉中间的一个 x x x后的答案
这个时候 x x x以前的数的 r a n k rank rank不变 x x x后面的数的 r a n k rank rank − 1 -1 1

所以预处理一个前缀和后缀的 m a x max max 表示前 i i i个 或后 i i i个数中 f j + r a n k j f_j+rank_j fj+rankj的最小值 j j j g g g值就可以通过 m a x ( p r e x − 1 , s u f x + 1 − 1 ) max(pre_{x-1},suf_{x+1}-1) max(prex1,sufx+11) 得出

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#pragma GCC optimize(2)
using namespace std;
const int N=2e5+5;
int n,m,x,head[N],tot,f[N],pre[N],suf[N];
int ans=N+1,query[N],g[N];
struct qaq{int x,val;
}a[N];
struct node{int to,next;
}edge[N<<1];
void add(int x,int y)
{edge[++tot]=(node){y,head[x]};head[x]=tot;
}
bool cmp(qaq x,qaq y){return x.val>y.val;}
inline void dp(int x,int fa)
{for(int i=head[x];i;i=edge[i].next){int qwq=edge[i].to;if(qwq==fa) continue;dp(qwq,x);}int cnt=0;for(register int i=head[x];i;i=edge[i].next){int qwq=edge[i].to;if(fa==qwq) continue;a[++cnt]=qaq{qwq,f[qwq]};}sort(a+1,a+cnt+1,cmp);for(int i=1;i<=cnt;i++)f[x]=max(f[x],a[i].val+i);  //推出f
}
inline void work(int x,int fa)
{int cnt=0;if(x!=1) a[++cnt]=qaq{-1,g[x]};  //rank要-1for(int i=head[x];i;i=edge[i].next){int qwq=edge[i].to;if(qwq==fa) continue;a[++cnt]=qaq{qwq,f[qwq]};}sort(a+1,a+cnt+1,cmp);pre[0]=suf[cnt+1]=0;for(int i=1;i<=cnt;i++)pre[i]=max(pre[i-1],a[i].val+i);for(int i=cnt;i>=1;i--)suf[i]=max(suf[i+1],a[i].val+i-1);//处理前缀后缀for(int i=1;i<=cnt;i++){int qwq=a[i].x;if(qwq>0) g[qwq]=max(pre[i-1],suf[i+1]);  //推出g}if(ans>pre[cnt]) ans=pre[cnt],m=0;if(ans==pre[cnt]) query[++m]=x;for(int i=head[x];i;i=edge[i].next){int qwq=edge[i].to;if(qwq==fa) continue;work(qwq,x);}
}
int main(){
//	freopen("news.in","r",stdin);
//	freopen("news.out","w",stdout);scanf("%d",&n);for(register int i=2;i<=n;i++){scanf("%d",&x);add(x,i);add(i,x);}dp(1,0);work(1,0);sort(query+1,query+m+1);printf("%d\n",ans+1);for(int i=1;i<=m;i++)printf("%d ",query[i]);	return 0;
} 

T 2 : J I H T2:JIH T2JIH的玩偶

在这里插入图片描述

分析:

因为只有从 x x x r o o t root root 那就直接倍增
用类似 R M Q RMQ RMQ的倍增维护 f a t h e r , m a x , m i n father,max,min father,max,min以及差
最后就倍增跳 每次取最大值和最小值的差 再更新父节点 就行了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=2e5+5;
int fa[20][N],minn[20][N],maxn[20][N],k[20][N],n,T;
int main(){
//	freopen("tree.in","r",stdin);
//	freopen("tree.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&maxn[0][i]);minn[0][i]=maxn[0][i];}for(int i=1,x,y;i<n;i++){scanf("%d%d",&x,&y);fa[0][x]=y;}for(int i=1;i<=18;i++)for(int j=1;j<=n;j++){int qwq=fa[i-1][j];fa[i][j]=fa[i-1][qwq];maxn[i][j]=max(maxn[i-1][j],maxn[i-1][qwq]);  //最大值minn[i][j]=min(minn[i-1][j],minn[i-1][qwq]);  //最小值k[i][j]=max(max(k[i][j],maxn[i-1][qwq]-minn[i-1][j]),max(k[i-1][qwq],k[i-1][j]));  //差值}scanf("%d",&T);while(T--){int x,y,res=0x3f3f3f3f,ans=0;scanf("%d%d",&x,&y);for(int i=0;y;i++,y>>=1)if(y&1){ans=max(ans,max(k[i][x],maxn[i][x]-res));  //取最后差值res=min(res,minn[i][x]);x=fa[i][x];  //更新fa}printf("%d\n",ans);}return 0;
} 

T 3 : T3: T3摘取作物

在这里插入图片描述

分析:

网络流费用流
每个点向它的行列连边 源点向每行连 汇点向每列连

S S S到左边 n n n个点容量 2 2 2 费用 0 0 0
右边 m m m个点到 T T T 容量 2 2 2 费用 0 0 0

因为
在这里插入图片描述
中间 n n n m m m 容量为 1 1 1 费用为 w i , j w_{i,j} wi,j
s p f a spfa spfa判断流通 然后跑最大费用增广路 直到没有增广路

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=(35<<1);
int n,m,S,T,w[N][N],f[N][N],ans;
int vis[N],dis[N],pre[N],G[N][N],q[N*10];
int spfa()
{memset(dis,138,sizeof(dis));memset(vis,0,sizeof(vis));int head=0,tail=1;q[1]=S;dis[S]=0;vis[S]=1;while(head<=tail){head++;int qwq=q[head];for(int i=1;i<=T;i++){if(qwq^i&&f[qwq][i]&&dis[qwq]+G[qwq][i]>dis[i]){pre[i]=qwq;dis[i]=dis[qwq]+G[qwq][i];if(!vis[i]){vis[i]=1;q[++tail]=i;}}vis[qwq]=0;}}if(dis[T]<0) return 0;int res=0x3f3f3f3f,qwq=T;while(qwq^S){res=min(res,f[pre[qwq]][qwq]);qwq=pre[qwq];}qwq=T;while(qwq^S){f[pre[qwq]][qwq]-=res;f[qwq][pre[qwq]]+=res;qwq=pre[qwq];}ans+=dis[T];return 1;
}
int main(){
//	freopen("pick.in","r",stdin);
//	freopen("pick.out","w",stdout);scanf("%d%d",&n,&m);S=n+m+1;T=n+m+2;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%d",&w[i][j]);f[i][j+n]=1;  //中间n到mG[i][j+n]=w[i][j];  //费用G[j+n][i]=-w[i][j];}for(int i=1;i<=n;i++)f[S][i]=2;  //源点到左边for(int i=n+1;i<=n+m;i++)f[i][T]=2;  //右边到汇点while(spfa());printf("%d",ans);return 0;
} 

T 4 : T4: T4公路维护

在这里插入图片描述
在这里插入图片描述

分析:

显然数据结构了 那就线段树

维护两个 l a z y T a g lazyTag lazyTag 加值和最大值 加值的优先级更大
其余就是每种情况 的线段树操作了

操作 1 1 1要先看区间是不是能走的 再减 看减完有没有 < = 0 <=0 <=0的 就把这段区间标为 i n f inf inf

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,m,I,ans;
struct SegmentTree{int lazyA,valA,lazyX,valX;bool broken;
}a[N<<2],qwq;
void up(SegmentTree &a,SegmentTree &b,SegmentTree &c)
{if(c.valA<b.valA) a.valA=c.valA,a.valX=c.valX;else a.valA=b.valA,a.valX=b.valX;a.broken=b.broken&c.broken;
}
void down(int x)
{a[x<<1].lazyA=max(a[x<<1].lazyA+a[x].lazyX,a[x].lazyA);a[x<<1|1].lazyA=max(a[x<<1|1].lazyA+a[x].lazyX,a[x].lazyA);a[x<<1].valA=max(a[x<<1].valA+a[x].lazyX,a[x<<1].lazyA);a[x<<1|1].valA=max(a[x<<1|1].valA+a[x].lazyX,a[x<<1|1].lazyA);a[x<<1].lazyX+=a[x].lazyX;a[x<<1|1].lazyX+=a[x].lazyX;a[x].lazyA=-0x3f3f3f3f;a[x].lazyX=0;
}
void build(int x,int l,int r)
{if(l==r){a[x].valA=I;a[x].valX=l;a[x].broken=1;a[x].lazyA=-0x3f3f3f3f;return;}int mid=(l+r)>>1;build(x<<1,l,mid);build(x<<1|1,mid+1,r);up(a[x],a[x<<1],a[x<<1|1]);
}
bool check(int x,int l,int r,int L,int R)  //看这段路能不能走
{if(L==l&&r==R) return a[x].broken;down(x);int mid=(l+r)>>1;bool res=1;if(R<=mid) res=check(x<<1,l,mid,L,R);else if(mid<L) res=check(x<<1|1,mid+1,r,L,R);else res=check(x<<1,l,mid,L,mid)&check(x<<1|1,mid+1,r,mid+1,R);up(a[x],a[x<<1],a[x<<1|1]);return res;
}
void add(int x,int l,int r,int L,int R,int val)  //加值
{if(L==l&&r==R){a[x].valA+=val;a[x].lazyX+=val;a[x].lazyA+=val;return;}down(x);int mid=(l+r)>>1;if(R<=mid) add(x<<1,l,mid,L,R,val);else if(mid<L) add(x<<1|1,mid+1,r,L,R,val);else add(x<<1,l,mid,L,mid,val),add(x<<1|1,mid+1,r,mid+1,R,val);up(a[x],a[x<<1],a[x<<1|1]);
}
void Repare(int x,int l,int r,int L,int R,int val)  //修复这段路
{if(L==l&&r==R){a[x].lazyA=max(a[x].lazyA,val);a[x].valA=max(a[x].valA,a[x].lazyA);return;}down(x);int mid=(l+r)>>1;if(R<=mid) Repare(x<<1,l,mid,L,R,val);else if(mid<L) Repare(x<<1|1,mid+1,r,L,R,val);else Repare(x<<1,l,mid,L,mid,val),Repare(x<<1|1,mid+1,r,mid+1,R,val);up(a[x],a[x<<1],a[x<<1|1]);
}
void UnRet(int x,int l,int r,int p)  //这段路不可修复
{if(l==r){a[x].valA=0x3f3f3f3f;a[x].broken=0;return;}down(x);int mid=(l+r)>>1;if(p<=mid) UnRet(x<<1,l,mid,p);else UnRet(x<<1|1,mid+1,r,p);up(a[x],a[x<<1],a[x<<1|1]);
}
void found(int x,int l,int r,int L,int R)  //看区间内有没有不能走的
{if(L==l&&r==R){if(!qwq.valX) qwq=a[x];else if(a[x].valA<qwq.valA) qwq=a[x];return;}down(x);int mid=(l+r)>>1;if(R<=mid) found(x<<1,l,mid,L,R);else if(mid<L) found(x<<1|1,mid+1,r,L,R);else found(x<<1,l,mid,L,mid),found(x<<1|1,mid+1,r,mid+1,R);up(a[x],a[x<<1],a[x<<1|1]);
}
int main(){
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);scanf("%d%d%d",&n,&m,&I);build(1,1,n);int E=n;while(m--){int op,l,r,x;scanf("%d%d%d%d",&op,&l,&r,&x);if(op==1){if(check(1,1,n,l,r)){ans++;add(1,1,n,l,r,-x);while(E){qwq.valX=0;found(1,1,n,l,r);if(qwq.valA<=0&&qwq.lazyA<=0)UnRet(1,1,n,qwq.valX);else break;E--;}}continue;}if(op==2){add(1,1,n,l,r,x);continue;}if(op==3){Repare(1,1,n,l,r,x);continue;}}printf("%d",ans);return 0;
}

这篇关于2021.7.13 提高B组模拟赛的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/164018

相关文章

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

键盘快捷键:提高工作效率与电脑操作的利器

键盘快捷键:提高工作效率与电脑操作的利器 在数字化时代,键盘快捷键成为了提高工作效率和优化电脑操作的重要工具。无论是日常办公、图像编辑、编程开发,还是游戏娱乐,掌握键盘快捷键都能带来极大的便利。本文将详细介绍键盘快捷键的概念、重要性、以及在不同应用场景中的具体应用。 什么是键盘快捷键? 键盘快捷键,也称为热键或快捷键,是指通过按下键盘上的一组键来完成特定命令或操作的方式。这些快捷键通常涉及同

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

如何提高 GitHub 的下载速度

如何提高 GitHub 的下载速度 文章目录 如何提高 GitHub 的下载速度1. 注册账号2. 准备好链接3. 创建仓库4. 在码云上下载代码5. 仓库更新了怎么办 一般来说,国内的朋友从 GitHub 上面下载代码,速度最大是 20KB/s,这种龟速,谁能忍受呢? 本文介绍一种方法——利用“码云”,可以大大提高下载速度,亲测有效。 1. 注册账号 去“码云”注册一

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

【算法专场】模拟(下)

目录 前言 38. 外观数列 算法分析 算法思路 算法代码 1419. 数青蛙 算法分析 算法思路 算法代码  2671. 频率跟踪器 算法分析 算法思路 算法代码 前言 在前面我们已经讲解了什么是模拟算法,这篇主要是讲解在leetcode上遇到的一些模拟题目~ 38. 外观数列 算法分析 这道题其实就是要将连续且相同的字符替换成字符重复的次数+