【学习笔记】CF627F Island Puzzle

好啊,树上贪心题。

首先可以用类似于拓扑排序的过程将无用的节点全部删掉。

具体的,如果两个叶子节点对应的值恰好相同,那么同时将叶子节点删去;如果其中一个叶子节点对应的是 0 0 0,并且与父节点交换后相同,因为操作是可逆的,那么花费 1 1 1的代价将 0 0 0往中间挪,同时删去叶子节点;如果两个叶子节点对应的都是 0 0 0,那么有挪和不挪两种情况,如果不挪可以把整颗树删完那么就结束了,否则可以认为此时两个 0 0 0同时往中间挪。

观察到此时连一条边只能改变一个环上的点,因此如果有解那么一定只有两个叶子节点,也就是形成了一条路径。那么我们将路径连起来就形成了一个环。观察到 0 0 0只会往一个方向移动,一个数向左/右挪动的距离就是被交换的次数,因此贪心即可。

然后就做完了。

复杂度 O ( n ) O(n) O(n)

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define inf 0x3f3f3f3f3f3f3f3f #define db double #define cpx complex<db> using namespace std; const int N=2e5+5; int n,a[N],b[N],totleaf,home[N],deg[N]; int aa[N],bb[N],cnt,rk[N]; ll res; vector<int>G[N]; queue<int>Q; vector<int>vec; void dfs(int u){ home[u]=1; aa[cnt]=a[u],bb[cnt]=b[u],cnt++; for(auto v:G[u]){ if(!home[v])dfs(v); } } ll solve(){ memset(rk,-1,sizeof rk); int p=0;while(p<cnt&&bb[p])p++; for(int i=0;i<cnt;i++)rk[aa[i]]=i; ll Min=inf,Max=-inf; for(int i=0;i<cnt;i++){ if(bb[i]){ ll D=(i-rk[bb[i]]+cnt)%cnt; Min=min(Min,D),Max=max(Max,D); } } if(Max-Min>1){ return inf; } if(Max==Min){ return Max*(cnt-1); } else{ int cnt1=0,cnt2=0; for(int i=(p+1)%cnt;i!=p;i=(i+1)%cnt){ ll D=(i-rk[bb[i]]+cnt)%cnt; if(D==Max)cnt1++; } for(int i=(p+1)%cnt;i!=p;i=(i+1)%cnt){ ll D=(i-rk[bb[i]]+cnt)%cnt; if(D==Max)cnt2++; else break; } if(cnt1!=cnt2)return inf; return Min*(cnt-1)+cnt1; } } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n;for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)cin>>b[i]; for(int i=1;i<n;i++){ int u,v;cin>>u>>v; G[u].pb(v),G[v].pb(u); deg[u]++,deg[v]++; } for(int i=1;i<=n;i++){ if(deg[i]==1){ totleaf++; Q.push(i); } } while(Q.size()){ int x=Q.front();Q.pop(); if(!a[x]&&!b[x])continue; if(a[x]==b[x]){ home[x]=1,totleaf--; for(auto y:G[x]){ if(!home[y]&&--deg[y]==1){ Q.push(y),totleaf++; } } } else if(a[x]==0){ int fa=0; for(auto y:G[x]){ if(!home[y]){ fa=y; } } if(a[fa]==b[x]){ swap(a[x],a[fa]),res++,home[x]=1,totleaf--; if(--deg[fa]==1){ Q.push(fa),totleaf++; } } } else if(b[x]==0){ int fa=0; for(auto y:G[x]){ if(!home[y]){ fa=y; } } if(b[fa]==a[x]){ swap(b[x],b[fa]),res++,home[x]=1,totleaf--; if(--deg[fa]==1){ Q.push(fa),totleaf++; } } } } if(totleaf==1){ cout<<0<<" "<<res<<"\n"; } else{ for(int i=1;i<=n;i++){ if(a[i]==0&&b[i]==0&&deg[i]==1)Q.push(i); } while(Q.size()){ int x=Q.front();Q.pop(); int fa=0; for(auto y:G[x]){ if(!home[y]){ fa=y; } } if(a[fa]==b[fa]){ swap(a[x],a[fa]),swap(b[x],b[fa]),home[x]=1,res+=2,totleaf--; if(--deg[fa]==1)Q.push(fa),totleaf++; } } if(totleaf>2){ cout<<-1<<"\n"; } else{ for(int i=1;i<=n;i++){ if(deg[i]==1&&!home[i]){ vec.pb(i); } } dfs(vec[0]); ll res1=solve(); reverse(aa,aa+cnt),reverse(bb,bb+cnt); res1=min(res1,solve()); if(res1==inf){ cout<<-1<<"\n"; } else{ cout<<vec[0]<<" "<<vec[1]<<" "<<res+res1<<"\n"; } } } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529987.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示