其实就是计蒜客上的模拟赛啦~
题目链接:T1
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;
}