【2018-07-03】集训模拟赛第一场题解

其实就是计蒜客上的模拟赛啦~

题目链接:T1

                 T2

                 T3   

T1题解:因为是一个斜着放的正方形锤子,我们不好处理,所以我们可以将方阵旋转45度,并且扩大一倍的密度,这样所有点的坐标都是整数,并且锤子也变成了正方形,直接用二维前缀和处理即可。

细节:(从1开始的下标)本来在(i,j)上的点,旋转之后的位置为(i+j-1,n-i+j)。

代码:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long LL;
int read()
{
	char c=getchar();int f=1,sum=0;
	while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
int n,k;
int a[4005][4005],sum[4005][4005];
bool vis[4005][4005];
void init()
{
	n=read();k=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	{
		int x=read();
		a[i+j-1][n-i+j]=x;
		vis[i+j-1][n-i+j]=1;
	}
	n=n*2-1;
	k=k*2-1;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
}
int solve()
{
	int ans=0;
	for(int i=k;i<=n;i++)
	{
		for(int j=k;j<=n;j++)
		{
			if(!vis[i][j]) continue;
			int ret=sum[i][j]-sum[i-k][j]-sum[i][j-k]+sum[i-k][j-k];
			ans=max(ans,ret);
		}
	}
	return ans;
}
int main()
{
	init();
	printf("%d\n",solve());
	return 0;
}

T2:题目要求统计所有简单路径的长度和,这个问题等价于每条边的长度乘上经过这条边的次数,而一条边的经过次数就是其两边点的个数的乘积,一遍dfs即可处理。修改时直接减去原来的结果加上新的结果即可。

代码:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long LL;
int read()
{
	char c=getchar();int f=1,sum=0;
	while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
int n,m,ans,bian[maxn];
int head[maxn],to[maxn<<1],nex[maxn<<1],val[maxn<<1],cnt;
void add(int u,int v,int w){to[++cnt]=v;nex[cnt]=head[u];val[cnt]=w;head[u]=cnt;}
LL sum[maxn],dp[maxn];
void dfs(int x,int fa)
{
	sum[x]=1;
	for(int i=head[x];i;i=nex[i])
	{
		if(to[i]==fa) continue;
		dfs(to[i],x);
		sum[x]+=sum[to[i]];
		dp[x]+=(dp[to[i]]+sum[to[i]]*(n-sum[to[i]])*(LL)val[i]);
	}
}
int main()
{
	n=read();
	for(int x,y,i=1;i<n;i++)
	{
		x=read();y=read();
		add(i+1,x,y);add(x,i+1,y);
		bian[i+1]=y;
	}
	dfs(1,0);
	printf("%lld\n",dp[1]);
	m=read();
	for(int i=1;i<=m;i++)
	{
		int a=read(),b=read();
		LL ans=dp[1]+sum[a]*(n-sum[a])*(b-bian[a]);
		bian[a]=b;
		printf("%lld\n",ans);
		dp[1]=ans;
	}
	return 0;
}

T3:我们用dp[i][j][k]表示在点(i,j)时,还有k次技能可以用时受到的最小伤害。

转移时,如果不用技能可以直接转移到dp[i+1][j][k]和dp[i][j+1][k]。

用了技能,我们就要用一个dfs直接转移到走了k步之后的位置,这样就不会牵扯到一个技能没放完就又放技能的问题。

代码:

#include<bits/stdc++.h>
#define maxn 100005
#define inf (1<<30)
#define mod 1000000007
using namespace std;
typedef long long LL;
int read()
{
	char c=getchar();int f=1,sum=0;
	while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
int n,m,t,k,hp,atk,ans=2147483647;
int a[505][505],dp[505][505][15];
void dfs(int x,int y,int t,int step,int atk,int dam)
{
	if(step==0)
	{
		dp[x][y][t]=min(dp[x][y][t],dam);
		return;
	}
	if(x+1<=n)
	{
		int nexatk=atk+a[x+1][y];
		dfs(x+1,y,t,step-1,nexatk,dam+(hp-1)/nexatk*a[x+1][y]);
	}
	if(y+1<=m)
	{
		int nexatk=atk+a[x][y+1];
		dfs(x,y+1,t,step-1,nexatk,dam+(hp-1)/nexatk*a[x][y+1]);
	}
	return;
}
int main()
{
	n=read();m=read();t=read();k=read();hp=read();atk=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	a[i][j]=read();
	memset(dp,0x3f,sizeof(dp));
	dp[1][1][t]=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	for(int l=t;l>=0;l--)
	{
		if(i+1<=n)
		dp[i+1][j][l]=min(dp[i+1][j][l],dp[i][j][l]+(hp-1)/atk*a[i+1][j]);
		if(j+1<=m)
		dp[i][j+1][l]=min(dp[i][j+1][l],dp[i][j][l]+(hp-1)/atk*a[i][j+1]);
		if(l>=1)
		dfs(i,j,l-1,k,atk,dp[i][j][l]);
	}
	for(int i=0;i<=t;i++)
	ans=min(ans,dp[n][m][i]);
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39791208/article/details/80896783
今日推荐