容易观察到一次操作是将 \(0\) 移动到一个相邻的节点上,而且操作可逆转。
所以对于不加边的情况方案是唯一的,直接模拟一下看看是不是相等的就好。
对于加一条边来说,我们先将 \(0\) 移动到目标位置,观察一下这种状态下他的答案长什么样子。
因为操作是可逆的,所以对于 \(0\) 出去再回来的话,他能改变的只有在加一条边形成的环上的点,而改变的方式则是先拿出一个点来,剩下的点转一下,再把拿出来的这个点塞回去。
那么我们可以找到需要改变的这个环,如果需要改变的点形不成一条路径,那么答案为 \(-1\)。
形成环的话我们找到这个环一开始被拿出来的那个点,这个时候路径就确定了,求最优解只需要把和开始时 \(0\) 向终点移动时的路径相反的部分减掉就可以了,复杂度 \(O(n)\)。
代码:
#include<bits/stdc++.h>
#define N 201001
#define MAX 2005
using namespace std;
typedef long long ll;
typedef double db;
const ll inf=1e18,mod=1e9+7;
inline void read(ll &ret)
{
ret=0;char c=getchar();bool pd=false;
while(!isdigit(c)){pd|=c=='-';c=getchar();}
while(isdigit(c)){ret=(ret<<1)+(ret<<3)+(c&15);c=getchar();}
ret=pd?-ret:ret;
return;
}
ll n,a[N],b[N],x,y,pos[N],p,s,t;
bool f[N];
vector<ll>v[N];
ll fat[N];
ll dep[N];
inline void dfs(ll ver,ll fa)
{
fat[ver]=fa;
for(int i=0;i<v[ver].size();i++)
{
ll to=v[ver][i];
if(to==fa)
continue;
dep[to]=dep[ver]+1;
dfs(to,ver);
}
return;
}
ll tmp[N];
ll lis[N],tot;
inline bool cmp(ll x,ll y)
{
return dep[x]>dep[y];
}
bool vis[N];
ll tp[N];
vector<ll>g;
ll h[N],cnt;
inline ll calc1()
{
cnt=0;
ll res=0;
if(g.size()==1)
{
ll now=g[0];
while(now!=p)
{
h[cnt++]=now;
if(!f[now])
res++;
else
res--;
now=fat[now];
}
res++;
h[cnt++]=p;
reverse(h,h+cnt);
for(int i=0;i<cnt;i++)
pos[b[h[i]]]=i;
set<ll>dt;
for(int i=1;i<cnt;i++)
{
ll d=0;
if(pos[tmp[h[i]]]<i)
d=i-pos[tmp[h[i]]];
else
d=i+cnt-1-pos[tmp[h[i]]];
dt.insert(d);
}
if(dt.size()>1)
{
puts("-1");
exit(0);
}
ll d=*dt.begin();
return res+(d-1)*cnt;
}
else
{
ll now=g[0];
while(now!=p)
{
h[cnt++]=now;
if(!f[now])
res++;
else
res--;
now=fat[now];
}
h[cnt++]=p;
reverse(h,h+cnt);
res++;
now=g[1];
while(now!=p)
{
h[cnt++]=now;
res++;
now=fat[now];
}
for(int i=0;i<cnt;i++)
pos[b[h[i]]]=i;
set<ll>dt;
for(int i=1;i<cnt;i++)
{
ll d=0;
if(pos[tmp[h[i]]]<i)
d=i-pos[tmp[h[i]]];
else
d=i+cnt-1-pos[tmp[h[i]]];
dt.insert(d);
}
if(dt.size()>1)
{
puts("-1");
exit(0);
}
ll d=*dt.begin();
return res+(d-1)*cnt;
}
}
inline ll calc2()
{
cnt=0;
ll res=0;
if(g.size()==1)
{
ll now=g[0];
h[cnt++]=p;
res++;
while(now!=p)
{
h[cnt++]=now;
res++;
now=fat[now];
}
for(int i=0;i<cnt;i++)
pos[b[h[i]]]=i;
set<ll>dt;
for(int i=1;i<cnt;i++)
{
ll d=0;
if(pos[tmp[h[i]]]<i)
d=i-pos[tmp[h[i]]];
else
d=i+cnt-1-pos[tmp[h[i]]];
dt.insert(d);
}
if(dt.size()>1)
{
puts("-1");
exit(0);
}
ll d=*dt.begin();
return res+(d-1)*cnt;
}
else
{
ll now=g[1];
while(now!=p)
{
h[cnt++]=now;
if(!f[now])
res++;
else
res--;
now=fat[now];
}
h[cnt++]=p;
reverse(h,h+cnt);
res++;
now=g[0];
while(now!=p)
{
h[cnt++]=now;
res++;
now=fat[now];
}
for(int i=0;i<cnt;i++)
pos[b[h[i]]]=i;
set<ll>dt;
for(int i=1;i<cnt;i++)
{
ll d=0;
if(pos[tmp[h[i]]]<i)
d=i-pos[tmp[h[i]]];
else
d=i+cnt-1-pos[tmp[h[i]]];
dt.insert(d);
}
if(dt.size()>1)
{
puts("-1");
exit(0);
}
ll d=*dt.begin();
return res+(d-1)*cnt;
}
}
signed main()
{
read(n);
for(int i=1;i<=n;i++)
{
read(a[i]);
if(!a[i])
s=i;
tmp[i]=a[i];
}
for(int i=1;i<=n;i++)
{
read(b[i]);
if(!b[i])
t=i;
}
for(int i=1;i<n;i++)
{
read(x);
read(y);
v[x].push_back(y);
v[y].push_back(x);
}
dfs(t,0);
ll now=s;
while(fat[now])
swap(tmp[fat[now]],tmp[now]),f[now]=true,now=fat[now];
bool flag=true;
for(int i=1;i<=n;i++)
flag&=tmp[i]==b[i];
if(flag)
{
printf("0 %lld\n",dep[s]);
exit(0);
}
for(int i=1;i<=n;i++)
{
if(tmp[i]!=b[i])
lis[++tot]=i;
}
sort(lis+1,lis+tot+1,cmp);
set<ll>st;
st.clear();
ll num=0;
for(int i=1;i<=tot;i++)
{
if(vis[lis[i]])
continue;
g.push_back(lis[i]);
num++;
ll now=lis[i];
vector<ll>c;
c.clear();
while(now&&!vis[now]&&tmp[now]!=b[now])
c.push_back(now),vis[now]=true,now=fat[now];
for(auto x:c)
tp[x]=now;
st.insert(now);
}
if(num>2||st.size()>1)
{
puts("-1");
exit(0);
}
p=*st.begin();
if(num==1)
x=p,y=lis[1];
else
x=g[0],y=g[1];
ll ans=dep[s];
now=p;
while(fat[now])
{
if(!f[now])
ans++;
else
ans--;
now=fat[now];
}
ans+=dep[p];
ans+=min(calc1(),calc2());
printf("%lld %lld %lld\n",min(x,y),max(x,y),ans);
exit(0);
}
标签:cnt,++,Island,Puzzle,fat,res,CF627F,now,ll
From: https://www.cnblogs.com/CelticBlog/p/16724059.html