Atcoder AGC004 题解

A - Divide a Cuboid

如果长宽高有一个是偶数答案就是0,否则输出最小的那一面的大小乘以1。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
LL a[3];
int main()
{
	scanf("%lld%lld%lld",&a[0],&a[1],&a[2]);
	sort(a,a+3);
	if(!(a[0]&1)||!(a[1]&1)||!(a[2]&1)) puts("0");
	else printf("%lld\n",a[0]*a[1]);
	return 0;
}

B - Colorful Slimes

枚举使用魔法的次数,假设使用 k k k次魔法,那么获得颜色 i i i的史莱姆的代价就是 [ i − k , i ] [i-k,i] [ik,i] a a a的最小值。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
int n;LL X,ans,a[2005],mi[2005][2005];
int main()
{
	scanf("%d%lld",&n,&X);
	for(RI i=1;i<=n;++i) scanf("%lld",&a[i]);
	for(RI i=1;i<=n;++i) {
		mi[i][i]=a[i];
		for(RI j=i+1;j<=n;++j) mi[i][j]=min(mi[i][j-1],a[j]);
	}
	for(RI i=0;i<n;++i) {
		LL kl=1LL*i*X;
		for(RI j=1;j<=n;++j) {
			int t=j-i;
			if(t>=1) kl+=mi[t][j];
			else kl+=min(mi[1][j],mi[t+n][n]);
		}
		if(i==0||kl<ans) ans=kl;
	}
	printf("%lld\n",ans);
	return 0;
}

C - AND Grid

人一蠢呐,就显形

以上那句应用长沙话读,描述做这题做了一个小时的我。

总之就是,除整张图边缘的一圈,将第一张图上所有奇数列都涂色,第二张图上所有偶数列都涂色,然后第一张图将第一行涂色,第二张图将最后一行涂色。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
char mp[505][505];
int a[505][505],b[505][505],n,m;
int main()
{
	scanf("%d%d",&n,&m);
	for(RI i=1;i<=n;++i) scanf("%s",mp[i]+1);
	for(RI i=1;i<=n;++i)
		for(RI j=1;j<=m;++j) if(mp[i][j]=='#') a[i][j]=b[i][j]=1;
	for(RI j=1;j<=m;++j) a[1][j]=b[n][j]=1;
	for(RI i=2;i<n;++i)
		for(RI j=2;j<m;++j)
			if(j&1) a[i][j]=1;
			else b[i][j]=1;
	for(RI i=1;i<=n;++i) {
		for(RI j=1;j<=m;++j)
			putchar(a[i][j]?'#':'.');
		puts("");
	}
	puts("");
	for(RI i=1;i<=n;++i) {
		for(RI j=1;j<=m;++j)
			putchar(b[i][j]?'#':'.');
		puts("");
	}
	return 0;
}

D - Teleporter

首先,如果1号节点不是走到自身,一定不行。因为如果1号节点走到了x号节点,那么说明x号节点走k-1次可以走到1号节点,走k次就走回x号节点,不满足条件。

那么这棵基环树上的环就被消掉了。然后贪心,如果一个点的子树里离它最远的点离它的距离为 k − 1 k-1 k1,那么就把这个点原来与父亲之间的边断掉,然后与1号节点相连。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=100005;
int n,K,ans,tot,h[N],ne[N],to[N],dep[N],dis[N];
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
void dfs(int x,int las) {
	dis[x]=dep[x];
	for(RI i=h[x];i;i=ne[i]) {
		dep[to[i]]=dep[x]+1,dfs(to[i],x);
		dis[x]=max(dis[x],dis[to[i]]);
	}
	if(las!=1&&x!=1&&dis[x]-dep[x]==K-1) ++ans,dis[x]=0;
}
int main()
{
	int x;
	n=read(),K=read();
	for(RI i=1;i<=n;++i) {
		x=read();
		if(i==1&&x!=1) x=1,++ans;
		if(i!=1) add(x,i);
	}
	dfs(1,0),printf("%d\n",ans);
	return 0;
}

E - Salvage Robots

我们看做机器人不动,我们动网格的边界和出口,设f(x1,y1,x2,y2)表示出口向上下左右移动的最大距离,我们就可以很方便的知道哪些行和列上的机器人被ban掉了,然后考虑是加入一行还是一列进行DP。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=105;
char mp[N][N];
int f[2][N][N][N],a[N][N],n,m,sx,sy,ans;
int gets(int x1,int y1,int x2,int y2)
	{return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];}
int main()
{
	scanf("%d%d",&n,&m);
	for(RI i=1;i<=n;++i) {
		scanf("%s",mp[i]+1);
		for(RI j=1;j<=m;++j) {
			a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+(mp[i][j]=='o');
			if(mp[i][j]=='E') sx=i,sy=j;
		}
	}
	for(RI x1=0,t=0;x1<sx;++x1,t^=1)
	  for(RI y1=0;y1<sy;++y1)
	    for(RI x2=0;x2<=n-sx;++x2)
	      for(RI y2=0;y2<=m-sy;++y2) {
	      	if(x1&&sx-x1>x2)
	      		f[t][y1][x2][y2]=max(f[t][y1][x2][y2],f[t^1][y1][x2][y2]+
	      			gets(sx-x1,max(sy-y1,y2+1),sx-x1,min(sy+y2,m-y1)));
	      	if(y1&&sy-y1>y2)
	      		f[t][y1][x2][y2]=max(f[t][y1][x2][y2],f[t][y1-1][x2][y2]+
	      			gets(max(sx-x1,x2+1),sy-y1,min(sx+x2,n-x1),sy-y1));
	      	if(x2<n&&sx+x2<=n-x1)
	      		f[t][y1][x2][y2]=max(f[t][y1][x2][y2],f[t][y1][x2-1][y2]+
	      			gets(sx+x2,max(sy-y1,y2+1),sx+x2,min(sy+y2,m-y1)));
	      	if(y2<m&&sy+y2<=m-y1)
	      		f[t][y1][x2][y2]=max(f[t][y1][x2][y2],f[t][y1][x2][y2-1]+
	      			gets(max(sx-x1,x2+1),sy+y2,min(sx+x2,n-x1),sy+y2));
	      	ans=max(ans,f[t][y1][x2][y2]);
	      }
	printf("%d\n",ans); 
	return 0;
}

F - Namori

树是一个二分图。

我们按照每个节点深度的奇偶染色,深度为奇数的点上放一枚美丽的硬币,每次可以把一枚硬币移动到相邻的空点上,要求将所有硬币移到空点上。

设硬币点权值为1,空地点权值为-1, s u m ( i ) sum(i) sum(i)表示以 i i i为根的子树的权值和,通过考虑这个节点的父亲节点,有多少个硬币要经过,我们知道树的情况下,答案是 ∑ a b s ( s u m ( i ) ) \sum abs(sum(i)) abs(sum(i))

如果有一个奇环,那么那条多出来的边选一次,会导致两边同时增加一枚硬币或者减少一枚硬币。我们知道,如果空地数和硬币数不相等,会导致无法完成任务,所以这条边被选只能用来使得空地数和硬币数相等,被选次数就确定了,是硬币数与空地数的差值除以2,同时,增加一下该边连接的两点的权值,具体看代码work2。

如果有一个偶环,假设从多的那条边的s端运x个硬币到t端,x可以是负数。初始令端的 k k k值为1,t端为-1,然后算出每个点子树内的k和,显然是0,1或者-1,那么答案就是:

a b s ( x ) + ∑ a b s ( k i x + s u m i ) abs(x)+\sum abs(k_ix+sum_i) abs(x)+abs(kix+sumi)

对于那些 k i k_i ki等于0的,直接算。先添加一个点0,然后对于 k i k_i ki等于-1,添加一个点 s u m i sum_i sumi,否则添加一个点 − s u m i -sum_i sumi,那么我们要找到一个离这些点总距离最短的点,显然是中位数。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
typedef long long LL;
const int N=100005;
int h[N],ne[N<<1],to[N<<1],sum[N],vis[N],k[N],b[N];
int n,m,tot,ss,tt,js,all;LL ans;
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
void dfs1(int x,int las) {
	sum[x]=-sum[las],all+=sum[x],vis[x]=1;
	for(RI i=h[x];i;i=ne[i]) {
		if(to[i]==las) continue;
		if(vis[to[i]]) tt=to[i],ss=x;
		else dfs1(to[i],x);
	}
}
void dfs2(int x,int las) {
	for(RI i=h[x];i;i=ne[i]) {
		if(to[i]==las||(x==ss&&to[i]==tt)||(x==tt&&to[i]==ss)) continue;
		dfs2(to[i],x);
		sum[x]+=sum[to[i]],k[x]+=k[to[i]];
	}
}
void work1() {
	if(all) {puts("-1");return;}
	dfs2(1,0);for(RI i=1;i<=n;++i) ans+=abs(sum[i]);
	printf("%lld\n",ans);
}
void work2() {
	if(all&1) {puts("-1");return;}
	sum[ss]-=all/2,sum[tt]-=all/2;
	ans+=abs(all/2);
	dfs2(1,0);for(RI i=1;i<=n;++i) ans+=abs(sum[i]);
	printf("%lld\n",ans);
}
void work3() {
	if(all) {puts("-1");return;}
	++k[ss],--k[tt],dfs2(1,0);
	b[++js]=0;
	for(RI i=1;i<=n;++i)
		if(!k[i]) ans+=abs(sum[i]);
		else if(k[i]==1) b[++js]=-sum[i];
		else b[++js]=sum[i];
	sort(b+1,b+1+js);
	int x=b[(js+1)/2];
	for(RI i=1;i<=js;++i) ans+=abs(x-b[i]);
	printf("%lld\n",ans);
}
int main()
{
	int x,y;
	n=read(),m=read();
	for(RI i=1;i<=m;++i) x=read(),y=read(),add(x,y),add(y,x);
	sum[0]=-1,dfs1(1,0);
	if(m==n-1) work1();
	else if(sum[ss]==sum[tt]) work2();
	else work3();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值