1 条题解

  • 2
    @ 2024-9-9 21:38:48

    我们充分发扬人类智慧。

    首先分析性质,如果一个有草莓的节点子树中存在有草莓的节点,那么我们就直接不管这个有草莓的节点,因为首先子树内的草莓已经会遍历这个点。

    另外,根据 P3320 [SDOI2015] 寻宝游戏 的结论,一个人肯定是按照 DFS 序大小遍历它需要遍历的草莓节点。

    注意到 n451n\le451,考虑模拟退火。每次随机交换遍历草莓的顺序,再把序列划分为前后两个集合分别给两个人走,取最大值,可以得到 7777 分。

    注意到这么做是极其不合理的,因为排序之后对答案影响较大的是每个点的 DFS 序而不是如何分配,于是我们模拟退火随机打乱出边,就可以获得 100100 分的好成绩。

    #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;
    }
    
    • 1

    信息

    ID
    543
    时间
    4000ms
    内存
    512MiB
    难度
    8
    标签
    递交数
    88
    已通过
    11
    上传者