简单MST
简单MST
n=1时,显然答案与顺序无关。现在假设size<n的树的答案与顺序都无关,我们来证明size=n的树的答案与顺序无关。
若根的儿子只有一个,那么得证。现假设根的儿子至少有两个。
我们用ANS(x)表示只有根和根的儿子x以及x的子树的子问题的答案,即从根出发,巡游完x的子树并回到根的答案。
我们用SZ(x)表示以x为根的子树的大小的两倍(即从根开始巡游x这个子树再回到根的天数)。
我们假定我们访问根的儿子的顺序是s1,s2…sm,那么总的答案就为ANS(s1)+SZ(s1)(SZ(s2)+SZ(s3)…+SZ(sm))+ANS(s2)+SZ(s2)(SZ(s3)+SZ(s4)+…SZ(sm))+…+ANS(sm)。
注意到总的答案为所有儿子的ANS的和再加上两两儿子之间SZ的积的和。由于儿子至少有两个,所以儿子的子树大小一定小于n,通过归纳法得知它们的ANS与顺序无关。两两儿子之间SZ的积的和也与顺序无关。所以size=n的树的总答案与顺序无关。

#include<bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define FOR(i,a,b) for(int i=(a);i<(b);i++)
typedef long long ll;
typedef pair<int, int>P;
#define X first
#define Y second
const int MAXN=1<<19,MAXM=1e7+5;
vector <P> V[MAXM];
int najbliz[MAXM];
int p[MAXN];
int n;
int root[MAXN],rnk[MAXN];
int bio[MAXM],ozn[MAXM];
int find(int x)
{
 if(root[x]==-1)
  return x;
 return root[x]=find(root[x]);
}
void merge(int a,int b)
{
 a=find(a);
 b=find(b);
 assert(a!=b);
 if(rnk[a]>rnk[b])
  root[b]=a;
 else if(rnk[b]>rnk[a])
  root[a]=b;
 else
 {
  rnk[a]++;
  root[b]=a;
 }
}
int main()
{
 scanf("%d",&n);
 memset(ozn,-1,sizeof ozn);
 REP(i,n)
 {
  scanf("%d",&p[i]);
  najbliz[p[i]]=p[i];
  if(ozn[p[i]]==-1)
   ozn[p[i]]=i;
 }
 for(int i=MAXM-2;i>=0;i--)
  if(!najbliz[i])
   najbliz[i]=najbliz[i+1];
 REP(i,n)
 {
  if(bio[p[i]]++)
   continue;
  if(najbliz[p[i]+1])
  {
   if(2*p[i]>=MAXM||najbliz[2*p[i]]!=najbliz[p[i]+1])
    V[najbliz[p[i]+1]-p[i]].push_back(P(i,ozn[najbliz[p[i]+1]]));
  }
  for(int j=2*p[i];j<MAXM&&najbliz[j];j+=p[i])
   if(j+p[i]>=MAXM||najbliz[j+p[i]]!=najbliz[j])
    V[najbliz[j]-j].push_back(P(i,ozn[najbliz[j]]));
 }
 memset(root,-1,sizeof root);
 ll rje=0;
 REP(i,MAXM)
  REP(j,(int)
   V[i].size())
 if(find(V[i][j].X)!=find(V[i][j].Y))
 {
  merge(V[i][j].X,V[i][j].Y);
  rje+=i;
 }
 REP(i,n)
  if(ozn[p[i]]==i)
  {
   assert(find(i)==find(0));
  }
 printf("%lld\n",rje);
 return 0;
}

相关文章: