1 条题解
-
2
我们充分发扬人类智慧。
首先分析性质,如果一个有草莓的节点子树中存在有草莓的节点,那么我们就直接不管这个有草莓的节点,因为首先子树内的草莓已经会遍历这个点。
另外,根据 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; }
信息
- ID
- 543
- 时间
- 4000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 88
- 已通过
- 11
- 上传者