20240609比赛总结
因为很多奇奇怪怪的问题,成功被小学生爆杀
T1 小奇挖矿2
https://gxyzoj.com/d/hzoj/p/767
最简单的方法,枚举m,然后暴力求解,显然会T
可以发现,当飞船停在有矿的点时,才会有贡献,所以可以枚举n
经过列举,可以得到当两个点的距离大于17时,就一定可以转移成功,所以可以开一个数组,记录到每个点之前的所有点的最大值,满足条件则直接转移
对于其他部分,则暴力判断是否能转移即可
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf=1e9+7;
int n,m,a[100005],b[100005],f[100005],ans,mx[100005];
struct node{
int a,b;
}x[100005];
bool cmp(node x,node y)
{
return x.b<y.b;
}
int lst[100005];
bool fl[105];
void solve2()
{
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i].a,&x[i].b);
}
fl[0]=fl[4]=fl[7]=1;
for(int i=8;i<=100;i++)
{
fl[i]=fl[i-4]|fl[i-7];
}
sort(x+1,x+n+1,cmp);
lst[1]=1;
for(int i=1;i<=n;i++)
{
int j=i-1;
for(;j>=0;j--)
{
int p=x[i].b-x[j].b;
if(p>18) break;
if(p>=18||fl[p])
{
f[i]=max(f[i],f[j]+x[i].a);
lst[i]=j;
}
}
if(x[i].b<18&&!fl[x[i].b]) continue;
f[i]=max(mx[j]+x[i].a,f[i]);
mx[i]=max(mx[i-1],f[i]);
ans=max(ans,f[i]);
}
printf("%d",ans);
}
int main()
{
scanf("%d%d",&n,&m);
solve2();
return 0;
}
T2 小奇的矩阵(matrix)
可以直接把平均数替换为
因为
此时,可以直接用一个三维的dp记录最小的平方和,然后再与和的平方相加即可
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf=1e9;
int T,n,m,a[35][35],dp[35][35][1805];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=0;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
for(int k=0;k<=(n+m-1)*30;k++)
{
dp[i][j][k]=inf;
}
}
}
dp[1][1][a[1][1]]=a[1][1]*a[1][1];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==1&&j==1) continue;
for(int k=a[i][j];k<=(n+m-1)*30;k++)
{
dp[i][j][k]=min(dp[i][j][k],min(dp[i-1][j][k-a[i][j]],dp[i][j-1][k-a[i][j]])+a[i][j]*a[i][j]);
}
}
}
int ans=inf;
for(int i=1;i<=(n+m-1)*30;i++)
{
ans=min(1ll*ans,1ll*dp[n][m][i]*(n+m-1)-i*i);
}
printf("%d\n",ans);
}
return 0;
}
T3 小奇的仓库(warehouse)
https://gxyzoj.com/d/hzoj/p/769
可以发现,因为m<16,所以至多只有四位会发生改变,前面的不会变
所以前面的直接暴力树形dp,后面的四位则另外开一个数组记录,再转移的过程中累加即可
最后暴力计算最后四位有没有值即可
代码:
#include<cstdio>
using namespace std;
int n,m,edgenum,head[100005];
struct edge{
int to,nxt,val;
}e[200005];
void add_edge(int u,int v,int w)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
e[edgenum].val=w;
head[u]=edgenum;
}
int size[100005],f1[100005],d[100005][20],f2[100005];
int id(int x)
{
return (x%16+16)%16;
}
void dfs(int u,int fa)
{
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to,w=e[i].val;
if(v==fa) continue;
dfs(v,u);
size[u]+=size[v];
f1[u]+=f1[v]+w*size[v];
for(int j=0;j<16;j++)
{
d[u][id(j+w)]+=d[v][j];
}
}
d[u][0]++;
size[u]++;
}
int p[100005][20];
void dfs2(int u,int fa)
{
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to,w=e[i].val;
if(v==fa) continue;
f2[v]=f2[u]+(n-2*size[v])*w;
for(int j=0;j<16;j++)
{
p[v][id(j+w)]=p[u][j]-d[v][id(j-w)]+d[v][id(j+w)];
}
dfs2(v,u);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs(1,0);
for(int i=0;i<16;i++)
{
p[1][i]=d[1][i];
}
f2[1]=f1[1];
dfs2(1,0);
for(int i=1;i<=n;i++)
{
// printf("%d\n",f2[i]);
int sum=0,a1=0,a2=0,a3=0,a4=0;
for(int j=0;j<16;j++)
{
sum+=p[i][j]*j;
if(j&1) a1+=p[i][j];
if(j&2) a2+=p[i][j];
if(j&4) a3+=p[i][j];
if(j&8) a4+=p[i][j];
}
// printf("%d %d %d %d\n",a1,a2,a3,a4);
if(m&1) a1=n-a1-1;
if(m&2) a2=n-a2-1;
if(m&4) a3=n-a3-1;
if(m&8) a4=n-a4-1;
f2[i]=f2[i]-sum+a1*1+a2*2+a3*4+a4*8;
printf("%d\n",f2[i]);
}
return 0;
}
[COCI2014-2015#1] Kamp
https://gxyzoj.com/d/hzoj/p/3720
出了道原题……
显然,如果回到原点,那么每个直接或间接连接根和关键点的边必然会走两次,所以可以先树形dp求出从1号点出发的路程,再进行up and down求出其他点
接下来考虑不回原点的情况,可以分为两种情况,一种是少走子树内一条链,二是少走子树外一条链
对于第一种情况,直接从儿子向父亲转移子树内最长链
对于第二种情况,分为两部分,一部分是有fa的子树外直接转移,另一部分则是fa除u外的其他子树的最长链
因为最长链可能在u的子树内,所以要记录次长链
代码:
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
int n,k,head[500006],edgenum;
struct edge{
int to,nxt;
ll val;
}e[1000006];
void add_edge(int u,int v,ll w)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
e[edgenum].val=w;
head[u]=edgenum;
}
int size[500005],d[500005];
ll dp[500005],f1[500005],f2[500005],g[500005],dp2[500005];
void dfs1(int u,int fa)
{
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to,w=e[i].val;
if(v==fa) continue;
dfs1(v,u);
size[u]+=size[v];
if(size[v])
{
dp[u]+=dp[v]+e[i].val;
if(f1[u]<=f1[v]+e[i].val)
{
g[u]=f1[u];
f1[u]=f1[v]+e[i].val;
d[u]=v;
}
else if(g[u]<=f1[v]+e[i].val)
{
g[u]=f1[v]+e[i].val;
}
}
}
}
void dfs2(int u,int fa)
{
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to,w=e[i].val;
if(v==fa) continue;
if(k-size[v])
{
dp2[v]=dp2[u]+dp[u]-dp[v];
if(!size[v]) dp2[v]+=e[i].val;
f2[v]=f2[u]+e[i].val;
if(v==d[u])
{
f2[v]=max(g[u]+e[i].val,f2[v]);
}
else
{
f2[v]=max(f1[u]+e[i].val,f2[v]);
}
}
dfs2(v,u);
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
for(int i=1;i<=k;i++)
{
int x;
scanf("%d",&x);
size[x]=1;
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++)
{
printf("%lld\n",dp[i]*2+dp2[i]*2-max(f1[i],f2[i]));
// printf("%lld %lld %lld %lld\n",dp[i],dp2[i],f1[i],f2[i]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本