AT3957-[AGC023F]01 on Tree【贪心,堆】
正題
題目鏈接:https://www.luogu.com.cn/problem/AT3957
題目大意
nnn個節(jié)點的一棵樹,每個節(jié)點有0/10/10/1。每次刪除一個根節(jié)點,然后把該節(jié)點的值填入序列,求最終序列的最小逆序?qū)?shù)量。
n≤2×105n\leq 2\times 10^5n≤2×105
解題思路
考慮一種貪心,開始每個節(jié)點作為一個單獨的聯(lián)通塊,每次選擇一個節(jié)點把它和它父節(jié)點的聯(lián)通快合并并且它的聯(lián)通快排在它父節(jié)點的后面。
顯然這樣的選擇可以構(gòu)成所有可能的序列,現(xiàn)在需要考慮選擇順序。設(shè)cntx,0/1cnt_{x,0/1}cntx,0/1?表示聯(lián)通塊xxx的0/10/10/1數(shù)量。
那么一個節(jié)點的兩個子節(jié)點兩個聯(lián)通塊x,yx,yx,y的順序,xxx排在yyy前面就會產(chǎn)生cntx,1×cnty,0cnt_{x,1}\times cnt_{y,0}cntx,1?×cnty,0?的貢獻(xiàn),所以如果xxx排在yyy前面那么有
cntx,1×cnty,0≤cnty,1×cntx,0cnt_{x,1}\times cnt_{y,0}\leq cnt_{y,1}\times cnt_{x,0}cntx,1?×cnty,0?≤cnty,1?×cntx,0?
化一下就是按照cntx,1cntx,0\frac{cnt_{x,1}}{cnt_{x,0}}cntx,0?cntx,1??從小到大選就好了。維護(hù)一個堆即可。
時間復(fù)雜度O(nlog?n)O(n\log n)O(nlogn)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=2e5+10; int n,f[N],fa[N],cnt[N][2]; long long ans; struct node{int x,w0,w1;node(int xx=0){x=xx;w0=cnt[xx][0];w1=cnt[xx][1];return;} }; bool operator<(node x,node y) {return 1ll*x.w1*y.w0>1ll*x.w0*y.w1;} priority_queue<node> q; int find(int x) {return (fa[x]==x)?x:(fa[x]=find(fa[x]));} int main() {scanf("%d",&n);for(int i=2;i<=n;i++)scanf("%d",&f[i]);for(int i=1;i<=n;i++){int x;scanf("%d",&x);cnt[i][x]++;fa[i]=i;if(i>1)q.push(node(i));}while(!q.empty()){node w=q.top();q.pop();int x=find(w.x);if(fa[x]!=x)continue;if(cnt[x][0]!=w.w0)continue;if(cnt[x][1]!=w.w1)continue;int y=find(f[x]);fa[x]=y;ans+=1ll*cnt[y][1]*cnt[x][0];cnt[y][0]+=cnt[x][0];cnt[y][1]+=cnt[x][1];if(y)q.push(node(y));}printf("%lld\n",ans);return 0; } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的AT3957-[AGC023F]01 on Tree【贪心,堆】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 出租房单间怎么设置路由器出租房无线路由器
- 下一篇: 怎样用路由器连接别人家的WiFi了如何用