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要-1
	for(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到m
			G[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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值