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] [i−k,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 k−1,那么就把这个点原来与父亲之间的边断掉,然后与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;
}