P5659-[CSP-S2019]树上的数【贪心】
正題
題目鏈接:https://www.luogu.com.cn/problem/P5659
題目大意
給出nnn個點的一棵樹,每個節(jié)點上有一個數字,你每次可以選擇一條邊刪除然后交換連接的兩個點的數字,在刪完所有數字后設pip_ipi?表示數字iii所在節(jié)點編號,要求使得排列ppp的字典序。
1≤n≤2000,1≤T≤101\leq n\leq 2000,1\leq T\leq 101≤n≤2000,1≤T≤10
解題思路
好陰間的題目
考慮對與一個點xxx的轉移肯定是如下圖類似的情況并且每個點連接的邊的刪除順序是獨立的(如果不獨立證明出現了環(huán),樹上顯然沒有環(huán))
 
 考慮上圖我們發(fā)現產生的數字搬運是x→1,1→2,2→3,3→xx\rightarrow 1,1\rightarrow 2,2\rightarrow 3,3\rightarrow xx→1,1→2,2→3,3→x,而邊的刪除順序是(x,3)→(x,2)→(x,1)(x,3)\rightarrow (x,2)\rightarrow (x,1)(x,3)→(x,2)→(x,1)(紅色箭頭畫反了)。不難發(fā)現的是因為必須刪除所有邊,所以最后肯定是xxx和它周圍的節(jié)點連接形成一個搬運環(huán)。
然后因為字典序最小,所以考慮貪心,如果快速對于(s,t)(s,t)(s,t)判斷sss上的數字能否搬運到ttt上。
先考慮在s→ts\rightarrow ts→t路徑上(不包括s,ts,ts,t)的節(jié)點xxx需要滿足的條件,記上一個節(jié)點為fafafa,下一個節(jié)點為yyy。
- 如果有數字走y→xy\rightarrow xy→x或者x→fax\rightarrow fax→fa搬運過那么不行
- 如果邊x→yx\rightarrow yx→y或者fa→xfa\rightarrow xfa→x正反都搬運過數字那么不行
- 如果此時對于節(jié)點xxx的邊中加上fa→yfa\rightarrow yfa→y這條邊后形成了一個環(huán)且不是所有xxx及其周圍的數字都在環(huán)上的話那么顯然不行。
- 如果邊(x,fa)(x,fa)(x,fa)已經要求在(x,y)(x,y)(x,y)之后再刪除了那么顯然不合法(也就是紅色的箭頭形成了環(huán))
對于根節(jié)點和終止節(jié)點則類似只是減少了上面第444個要求因為顯然這條邊必須是最早/最晚刪除的。
然后用類似鏈表的結構來維護每個節(jié)點連出去邊的情況來判斷后兩個條件,前兩個條件則很好判斷。并且如果保證了對于每個節(jié)點xxx都滿足它上面的數字不會搬回自己所在處就可以保證所有邊必須刪除了。
然后每次從未確定的最小的數字所在節(jié)點出發(fā)搜索所有合法的終止節(jié)點然后拿最小值再沿路更新即可。
時間復雜度:O(Tn2)O(Tn^2)O(Tn2)
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=2100; struct node{int to,next; }a[N<<1]; int T,n,tot,ls[N],p[N],d[N],d1[N],d2[N],fa[N]; int from[N],got[N],e[N][N],las[N][N],nxt[N][N]; bool v[N]; void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;d[y]++;return; } void dfs(int x,int root){for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa[x])continue;v[y]=1;if(x!=root){if(e[fa[x]][x]==fa[x]||e[x][y]==x)v[y]=0;//反向搬運過 if(e[fa[x]][x]==0||e[x][y]==0)v[y]=0;//被別的方向走過if(nxt[x][y]==from[x]&&las[x][fa[x]]==got[x]&&d[x]*2+d1[x]+d2[x]-2>0)v[y]=0;//構成一條偏序鏈 if(nxt[x][y]==fa[x])v[y]=0;//構成環(huán) }else{if(e[x][y]==x)v[y]=0;//反向搬運過 if(e[x][y]==0)v[y]=0;//被別的方向走過if(from[x]&&nxt[x][y]==from[x]&&d[x]+d1[x]+d2[x]-1>0)v[y]=0;//構成一條偏序鏈 }v[y]&=v[x];fa[y]=x;dfs(y,root);}if(x==root)v[x]=0;else{if(from[x])v[x]=0;if(got[x]&&nxt[x][got[x]]==fa[x]&&d[x]+d1[x]+d2[x]-1>0)v[x]=0;}return; } int main() {scanf("%d",&T);while(T--){scanf("%d",&n);tot=0;memset(d,0,sizeof(d));memset(ls,0,sizeof(ls));memset(d1,0,sizeof(d1));memset(d2,0,sizeof(d2));memset(got,0,sizeof(got));memset(from,0,sizeof(from));for(int i=1;i<=n;i++)scanf("%d",&p[i]);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);e[x][y]=e[y][x]=-1;las[x][y]=nxt[x][y]=y;las[y][x]=nxt[y][x]=x;}for(int i=1;i<=n;i++){int x=p[i];for(int j=1;j<=n;j++)fa[j]=0;v[x]=1;dfs(x,x);int y=0;for(int j=1;j<=n;j++)if(v[j]){y=j;break;}printf("%d ",y);from[y]=fa[y];while(fa[y]!=x){if(e[fa[y]][y]==-1){e[fa[y]][y]=e[y][fa[y]]=fa[y];d[y]--;d2[y]++;d[fa[y]]--;d1[fa[y]]++;}else{e[fa[y]][y]=e[y][fa[y]]=0;d1[y]--;d2[fa[y]]--;}int z=y;y=fa[y];las[y][nxt[y][z]]=las[y][fa[y]];nxt[y][las[y][fa[y]]]=nxt[y][z];//數字fa->y->z,所以y->fa的連接y->z }if(e[fa[y]][y]==-1){e[fa[y]][y]=e[y][fa[y]]=fa[y];d[y]--;d2[y]++;d[fa[y]]--;d1[fa[y]]++;}else{e[fa[y]][y]=e[y][fa[y]]=0;d1[y]--;d2[fa[y]]--;}got[x]=y;}putchar('\n');}return 0; }總結
以上是生活随笔為你收集整理的P5659-[CSP-S2019]树上的数【贪心】的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 吉林市有什么好玩的地方 吉林市景点介绍
- 下一篇: 黄海波个人资料简历 黄海波简介
