Luogu P7276 送给好友的礼物 题解
我们充分发扬人类智慧。
首先分析性质,如果一个有草莓的节点子树中存在有草莓的节点,那么我们就直接不管这个有草莓的节点,因为首先子树内的草莓已经会遍历这个点。
另外,根据 P3320 [SDOI2015] 寻宝游戏 的结论,一个人肯定是按照 DFS 序大小遍历它需要遍历的草莓节点。
注意到 ,考虑模拟退火。每次随机交换遍历草莓的顺序,再把序列划分为前后两个集合分别给两个人走,取最大值,可以得到 分。
注意到这么做是极其不合理的,因为排序之后对答案影响较大的是每个点的 DFS 序而不是如何分配,于是我们模拟退火随机打乱出边,就可以获得 分的好成绩。
#include <bits/stdc++.h>
using namespace std;
struct val
{
int v,p;
}w[500];
int n,m,u,v,h[500],a[500],c[500],f[500],b[500],d[500][500],dfn[500],ans=1e9,cnt=0,dfc=0;
vector<int>s[500],tmp[500];
mt19937 rd(time(0));
bool cmp(struct val a,struct val b)
{
return a.p<b.p;
}
void dfs1(int x,int fa)
{
for(int i=0;i<s[x].size();i++)
if(s[x][i]!=fa)dfs1(s[x][i],x),c[x]|=c[s[x][i]],f[x]|=c[s[x][i]];
}
void dfs2(int x,int fa,vector<int>s[])
{
dfn[x]=++dfc;
for(int i=0;i<s[x].size();i++)
if(s[x][i]!=fa)dfs2(s[x][i],x,s);
}
int cal(vector<int>s[])
{
dfc=0,dfs2(1,0,s);
for(int i=1;i<=m;i++)w[i].v=a[i],w[i].p=dfn[a[i]];
sort(w+1,w+m+1,cmp);
int la=0,ra=d[1][w[1].v]+d[w[m].v][1],mi=1e9,pr=1e9;
for(int i=1;i<m;i++)ra+=d[w[i].v][w[i+1].v];
mi=ra;
for(int i=1;i<m;i++)
{
la=d[1][w[1].v]+d[w[i].v][1];
for(int j=1;j<i;j++)la+=d[w[j].v][w[j+1].v];
ra=d[1][w[i+1].v]+d[w[m].v][1];
for(int j=i+1;j<m;j++)ra+=d[w[j].v][w[j+1].v];
if(max(la,ra)>pr)break;
mi=min(mi,max(la,ra)),pr=max(la,ra);
}
return mi;
}
void sa()
{
double t=2e5,k=0.99;
while(t>=1e-5)
{
if((double)clock()/CLOCKS_PER_SEC>3.5)return;
int c=t/1000.0*(double)m+1,del=0,now=0;
for(int i=1;i<=n;i++)tmp[i]=s[i];
for(int i=1;i<=c;i++)
{
int x=rd()%n+1;
shuffle(s[x].begin(),s[x].end(),rd);
}
now=cal(s),del=now-cal(tmp),ans=min(ans,now);
if(del>=0)
{
double p=exp(-del/t);
if((double)rd()/UINT_MAX>=p)
for(int i=1;i<=m;i++)s[i]=tmp[i];
}
t*=k;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j)d[i][j]=1e9;
else d[i][j]=0;
for(int i=1;i<=n-1;i++)scanf("%d%d",&u,&v),d[u][v]=d[v][u]=1,s[u].push_back(v),s[v].push_back(u);
for(int i=1;i<=m;i++)scanf("%d",&a[i]),c[a[i]]=1;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
dfs1(1,0),cnt=0;
for(int i=1;i<=m;i++)
if(!f[a[i]])a[++cnt]=a[i];
m=cnt;
for(int i=1;i<=n;i++)c[i]=0;
for(int i=1;i<=m;i++)c[a[i]]=1;
while((double)clock()/CLOCKS_PER_SEC<3.5)sa();
printf("%d\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探