2021.7.14 提高B组模拟赛

2021.7.14 2021.7.14 2021.7.14 模拟赛 Ⅲ Ⅲ
目录:

T1.树的直径
T2.积木
T3.软件公司
T4.我图呢

T 1 : T1: T1树的直径

在这里插入图片描述

分析:

看到树上距离 直接考虑 l c a lca lca
我们可以看出一条性质 : : 新直径的一端一定是旧直径的一端

那根据这一点 可以先随便从 2 , 3 , 4 2,3,4 2,3,4中选两点做起始直径 然后枚举一个点 x x x
看是起点到 x x x距离长 还是终点到 x x x距离长 就更新直径的起点或终点
最后直径取 m a x max max即可 离线搞一搞就可以 O ( m l o g n ) O(mlogn) O(mlogn)

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
int m,num,dep[N<<2],fa[N<<2][21],son[N<<2],ans[N<<2];
int dis,dis2,dis3;
int LCA(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[fa[x][i]]>=dep[y])
			x=fa[x][i];
	if(x==y) return x;
	for(int i=20;i>=0;i--)
		if(fa[x][i]^fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	return fa[x][0];
}
int Dis(int x,int y)
{
	int qwq=LCA(x,y);
	int res=dep[x]+dep[y]-(dep[qwq]<<1);
	return res;
}
void add(int x)
{
	dep[++num]=dep[x]+1;
	fa[num][0]=x;
	for(int i=1;i<=20;i++)
		fa[num][i]=fa[fa[num][i-1]][i-1];
}
int main(){
	scanf("%d",&m);
	fa[2][0]=fa[3][0]=fa[4][0]=1;
	dep[1]=0; num=4;
	dep[2]=dep[3]=dep[4]=1;
	for(int i=1;i<=m;i++)
	{
		int x;
		scanf("%d",&x);
		son[i]=num+1;
		add(x); add(x);
	}
	int s=2,t=4;
	dis=Dis(s,t);
	for(int i=1;i<=m;i++)
	{
		dis2=Dis(s,son[i]);
		dis3=Dis(t,son[i]);
		if(dis3>=dis&&dis3>=dis2)
		{
			s=son[i]; ans[i]=dis3;
			dis=dis3;
		}
		else if(dis2>=dis&&dis2>=dis3)
		{
			t=son[i]; ans[i]=dis2;
			dis=dis2;
		}
		else ans[i]=dis;
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}

T 2 : T2: T2积木

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

分析:

可以发现 第 x x x块积木高度 肯定是第 x − 1 x-1 x1块积木高度 + 1 , − 1 +1,-1 +1,1或不变
f i , j f_{i,j} fi,j为第 i i i块积木高度为 j j j的方案数 方程很简单:
f i , j = f i − 1 , j − 1 + f i − 1 , j + f i − 1 , j + 1 f_{i,j}=f_{i-1,j-1}+f_{i-1,j}+f_{i-1,j+1} fi,j=fi1,j1+fi1,j+fi1,j+1
M L E MLE MLE 就开滚动数组了 i i i表示状态 0 / 1 0/1 0/1

这样还会 T T T 因为是水解 开 O 2 O2 O2 r e g i s t e r register register就可以了
正解数论

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#pragma GCC optimize(2) 
#define reg register
using namespace std;
typedef long long ll;
const int N=2e4+5,Mod=1000000007;
ll n,a[N],h,x,f[2][N<<1];
int main(){
//	freopen("brick.in","r",stdin);
//	freopen("brick.out","w",stdout);
	scanf("%lld",&n);
	for(reg int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	h=-1;
	for(reg int i=1;i<=n;i++)
	{
		if(i<=(n>>1)+1) h++;
		else h--;
		memset(f[x],0,sizeof f[x]);
		if(i==1)
		{
			if(a[i]<=0) f[x][0]=1;
			else break;
		}
		else if(a[i]==-1)
			for(reg int j=h;j>=0;j--)
				f[x][j]=(f[x^1][j+1]+f[x^1][j]+f[x^1][j-1])%Mod;
		else
			f[x][a[i]]=(f[x^1][a[i]+1]+f[x^1][a[i]]+f[x^1][a[i]-1])%Mod;
		x^=1;
	}
	printf("%lld",f[x^1][0]);
	
	return 0;
}

T 3 : T3: T3软件公司

在这里插入图片描述

分析:

60 % 60\% 60%直接 O ( n m 4 ) O(nm^4) O(nm4) d p dp dp就行了

for(int i=1;i<=n;i++)
	for(int j=0;j<=m;j++)
	for(int k=0;k<=m;k++)
		for(int s=0;s<=j;s++)
			for(int t=0;t<=k;t++)
				f[i][j][k]=min(f[i][j][k],max(f[i-1][j-s][k-t],s*a[i]+t*b[i]));

100 % 100\% 100% d p : dp: dp
f i , j f_{i,j} fi,j表示前 i i i个人 1 1 1项目做了 j j j 2 2 2项目最多能做的个数
f i , j = m a x { f i , j , f i − 1 , j − k + ( a n s − k × A i ) / B i } ∑ k = 0 j f_{i,j}=max\{f_{i,j},f_{i-1,j-k}+(ans-k\times A_i)/B_i\}\sum_{k=0}^j fi,j=max{fi,j,fi1,jk+(ansk×Ai)/Bi}k=0j
二分答案就好了

CODE:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=205;
int n,m,f[N][N];
struct qaq{
	int a,b;
}qwq[N];
int main(){
//	freopen("company.in","r",stdin);
//	freopen("company.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&qwq[i].a,&qwq[i].b);
	int l=0,r=100;
	while(l<r)
	{
		int mid=(l+r)>>1;
		memset(f,-127,sizeof f);
		f[0][0]=0;
		for(int i=1;i<=n;i++)
		for(int j=0;j<=m;j++)
			for(int k=0;k<=j;k++)
			{
				if(mid-k*qwq[i].a<0) break;
				f[i][j]=max(f[i][j],f[i-1][j-k]+(mid-k*qwq[i].a)/qwq[i].b);
			}
		if(f[n][m]>=m) r=mid;
		else l=mid+1; 
	}
	printf("%d",r);
	
	return 0;
}

T 4 : T4: T4我图呢

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

分析:

二分图最大独立集了 把权值都加上 i n f inf inf
然后黑白染色 构造二分图
对于 Q 1 Q1 Q1 就是求总权值和减去最小割
T T T的最大点权和 就是不割的边的边权和 也就是总的权值减去最小割
网络流跑一跑就好了

p s : ps: ps一开始写了另一种写法 答案和本地跑是一样的 评测出来就是 w a wa wa 不知道为啥 q a q qaq qaq 换写法写调了一天了

CODE (本地过 评测 W A WA WA):

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int N=305;
const ll inf=1e9,INF=1e18;
int n,m,S,T,dep[N],x[N],y[N],qaq[N],vis[N],ovo[N];
int ToT=1,tot,Head[N],head[N];
ll w[N],ans,Ans;
struct node{
	int to,next;
	ll val;
}edge[N*N<<1],a[N*N<<1];
void addEdge(int x,int y){
	edge[++tot]=(node){y,Head[x]};
	Head[x]=tot;
}
void add(int x,int y,ll k)
{
	a[++ToT]=(node){y,head[x],k};
	head[x]=ToT;
	a[++ToT]=(node){x,head[y],0};
	head[y]=ToT;
}
void work(int x,int st)
{
	ovo[x]=st;
	if(st==0) add(S,x,w[x]);
	else add(x,T,w[x]);
	for(int i=Head[x];i;i=edge[i].next)
	{
		int qwq=edge[i].to;
		if(ovo[qwq]==-1) work(qwq,st^1);
	}
}
bool bfs()
{
	memset(dep,0,sizeof dep);
	queue<int> q;
	q.push(S);
	dep[S]=1;
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			int qwq=a[i].to;
			if(dep[qwq]==0&&a[i].val>0) 
			{
				dep[qwq]=dep[x]+1;
				q.push(qwq);
			}
		}
	}
	return dep[T]!=0;
}
ll Dinic(int x,ll k)
{
	if(x==T) return k;
	ll link=0;
	for(int i=qaq[x];i;i=a[i].next)
	{
		int qwq=a[i].to;
		if(dep[qwq]==dep[x]+1&&a[i].val)
		{
			ll res=Dinic(qwq,min(a[i].val,k));
			k-=res;
			link+=res;
			a[i].val-=res;
			a[i^1].val+=res;
			if(!k) break;
		}
	}
	if(!link) dep[x]=0;
	return link;
}
void getS(int x)
{
	vis[x]=1;
	for(int i=head[x];i;i=a[i].next)
	{
		int qwq=a[i].to;
		if(a[i].val&&!vis[qwq])
			getS(qwq);
	}
}

int main()
{
//	freopen("graph.in","r",stdin);
//	freopen("graph.out","w",stdout);
	scanf("%d%d",&n,&m);
	ans=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&w[i]);
		w[i]+=inf;
		ans+=w[i];
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		addEdge(x[i],y[i]);
		addEdge(y[i],x[i]);
	}
	memset(ovo,-1,sizeof(ovo));
	S=n+1;T=n+2;
	for(int i=1;i<=n;i++) 
		if(ovo[i]==-1) work(i,0);
	for(int i=1;i<=m;i++)
	{
		if(ovo[x[i]]==1) swap(x[i],y[i]);
		add(x[i],y[i],INF);
	}
	while(bfs())
	{
		for(int i=1;i<=T;i++)
			qaq[i]=head[i];
		ans-=Dinic(S,INF);
	}
	memset(vis,0,sizeof vis);
	Ans=0;
	getS(S);
	for(int i=1;i<=n;i++)
		if(ovo[i]^vis[i]) 
			Ans++;
	printf("%lld %lld\n",Ans,ans%inf); 
	for(int i=1;i<=n;i++)
		printf("%d",(ovo[i]^vis[i]));
	return 0;
} 

A C AC AC CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const ll inf=1e9,INF=1e18;
const int N=305,M=2e5+5;
ll n,m,w[N],head[N],qaq[N],frt[M],flow[M],to[M],next[M],val[M];
ll ovo[N][N],ans,tot=1,S,T,col[N]; 
void color(ll x,ll k)
{
	col[x]=k;
	for(ll i=1;i<=ovo[x][0];i++)
	{
		ll qwq=ovo[x][i];
		if(!col[qwq]) color(qwq,3-k);
	}
}
void add(ll x,ll y,ll k)
{
	frt[++tot]=x;
	to[tot]=y;
	val[tot]=k;
	next[tot]=head[x];
	flow[tot]=0;
	head[x]=tot;
}
queue<ll> q,clear;
ll dep[N],vis[N];
ll bfs()
{
	memset(vis,0,sizeof(vis));
	vis[S]=1;
	memset(dep,0,sizeof(dep));
	q=clear;
	q.push(S);
	while(!q.empty())
	{
		ll x=q.front();
		q.pop();
		for(ll i=head[x];i;i=next[i])
		{
			ll qwq=to[i];
			if(val[i]>flow[i]&&(!vis[qwq]))
			{
				vis[qwq]=1;
				dep[qwq]=dep[x]+1;
				q.push(qwq);
			}
		}
	}
	return dep[T];
}
ll dinic(ll x,ll maxflow)
{
	if(maxflow==0||x==T) return maxflow;
	ll link=0;
	for(ll i=qaq[x];i;i=next[i])
	{
		ll qwq=to[i];
		if(dep[x]+1==dep[qwq])
		{
			ll res=dinic(qwq,min(maxflow,val[i]-flow[i]));
			if(res)
			{
				link+=res;
				maxflow-=res;
				flow[i]+=res;
				flow[i^1]-=res;
				if(!maxflow) break;
			}
		}
	}
	return link;
}
ll work(ll sum)
{
	ll ans=sum;
	while(bfs())
	{
		for(ll i=1;i<=T;i++)
			qaq[i]=head[i];
		ans-=dinic(S,INF);
	}
	return ans;
}
ll ins[N],kel[N];
void getS()
{
	q=clear;
	q.push(S);
	ins[S]=1;
	while(!q.empty())
	{
		ll x=q.front();
		q.pop();
		for(ll i=head[x];i;i=next[i])
		{
			ll qwq=to[i];
			if(val[i]>flow[i]&&(!ins[qwq]))
			{
				ins[qwq]=1;
				q.push(qwq);
			}
		}
	}
}
int main(){
//	freopen("graph.in","r",stdin);
//	freopen("graph.out","w",stdout);
	ll sum=0;
	scanf("%lld%lld",&n,&m);
	S=n+1;T=n+2;
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&w[i]);
		w[i]+=inf;
		sum+=w[i];
	} 
	for(ll i=1;i<=m;i++)
	{
		ll x,y;
		scanf("%lld%lld",&x,&y);
		ovo[x][++ovo[x][0]]=y;
		ovo[y][++ovo[y][0]]=x;
	}
	for(ll i=1;i<=n;i++)
		if(!col[i]) color(i,1);
	for(ll i=1;i<=n;i++)
	{
		if(col[i]==1)
			add(S,i,w[i]),add(i,S,0);
		if(col[i]==2)
			add(i,T,w[i]),add(T,i,0);
	}
	for(ll i=1;i<=n;i++)
		if(col[i]==1)
		{
			for(ll j=1;j<=ovo[i][0];j++)
			{
				ll awa=ovo[i][j];
				add(i,awa,INF);
				add(awa,i,0);
			}
		}
	ans=work(sum);
	getS();
	for(ll i=2;i<=tot;i++)
	{
		if(i&1) continue;
		if(ins[frt[i]]!=ins[to[i]])
		{
			if(frt[i]==S||frt[i]==T) kel[to[i]]=1;
			if(to[i]==S||to[i]==T) kel[frt[i]]=1;
		}
	}
	ll Ans=0;
	for(ll i=1;i<=n;i++)
		Ans+=(kel[i]^1);
	printf("%lld %lld\n",Ans,ans%inf);
	for(ll i=1;i<=n;i++)
		putchar((kel[i]^1)+'0');
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值