【HDU - 6203】ping ping ping(lca+贪心思想,对lca排序,树状数组差分)
題干:
給出一個n+1個點的樹,以及p個點對,需要斷開一些點,使得這p個點對路徑不連通。輸出應該斷開的最少點數(shù)。
解題報告:
從那p個點對入手的話:首先考慮只有一對點的話,肯定是這條路徑上的隨便一個點都可以。兩個點對呢?肯定是有交點就選交點,沒交點就只能破壞兩個點了。多個點對呢?比較難考慮了。也比較難想到正解:貪心思維,破壞兩個點的LCA是最佳的。
從破壞的點入手:破壞一個點,可以影響到的是所有經(jīng)過他的所有的路徑,轉(zhuǎn)化到有根樹中,不難想到這個點就是lca。
當然這個題到這里還不夠,還需要加點貪心的思想,對p個點對按照他們的lca深度從大到小排序,如果某個點需要被破壞,那么它的所有子節(jié)點(子樹)都可以不再需要破壞別的點了(因為它的子節(jié)點到別的子節(jié)點肯定是要經(jīng)過該點的,要注意這個前提是lca是排好序的。所以對應的操作就是對子樹的所有節(jié)點都+1。
為什么要排序呢?因為這樣就能保證,當前處理到第i對點u,v,如果這個點所在任意子樹的某祖先x如果有被標記過,那么這條路徑一定就不用管了,因為uv的lca一定深度小于x,也就是說你u想到達v,一定要經(jīng)過x,又因為x已經(jīng)被破壞了,所以這對點直接不用管了,真神奇的想法!!
所以接下來問題就是判斷現(xiàn)在在(u,v)之間的路徑上有沒有被破壞的點(也就是查詢u和v這兩點的值是不是>0的),如果沒有的話那么此時就要破壞這個lca點,如果有的話就直接continue。
所以dfs序來維護子樹,然后用樹狀數(shù)組差分來維護區(qū)間和,查詢的時候查詢單點值。(當然,這后一步可以直接用線段樹來取代)
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 2e4 + 5; int n,in[MAX],out[MAX],clk,dep[MAX],id[MAX],c[MAX*10]; int fa[MAX][33]; vector<int> vv[MAX]; struct Node {int a,b,lca;bool operator <(const Node & bb)const {return dep[lca] > dep[bb.lca];} } p[MAX*5]; int lowbit(int x){ return x&(-x); } int query(int pos) {int res = 0;while(pos > 0) {res += c[pos];pos -= lowbit(pos);}return res; } void update(int pos,int val) {while(pos < MAX) {c[pos] += val;pos += lowbit(pos);} } void dfs(int cur,int rt,int deep) {in[cur] = ++clk;dep[cur] = deep;fa[cur][0]=rt;id[clk] = cur;int up = vv[cur].size();for(int i = 1; i<=31; i++) {fa[cur][i] = fa[fa[cur][i-1]][i-1];}for(int i = 0; i<up; i++) {int v = vv[cur][i];if(v == rt) continue;dfs(v,cur,deep+1);}out[cur] = clk; } int lca(int u,int v) {if(dep[v] > dep[u]) swap(u,v);for(int i = 31; i>=0; i--) {if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];}if(u == v) return u;for(int i = 31; i>=0; i--) {if(fa[u][i] != fa[v][i]) u = fa[u][i],v=fa[v][i];}return fa[u][0]; } int main() {while(~scanf("%d",&n)) {int a,b;clk=0;memset(fa,0,sizeof fa);memset(c,0,sizeof c);for(int i = 0; i<=n+1; i++) vv[i].clear();for(int i = 1; i<=n; i++) {scanf("%d%d",&a,&b);a++,b++;vv[a].pb(b);vv[b].pb(a);}dfs(1,0,1);int q;scanf("%d",&q);for(int i = 1; i<=q; i++) {scanf("%d%d",&a,&b);a++,b++;p[i].a = a,p[i].b = b,p[i].lca = lca(a,b);}sort(p+1,p+q+1);int ans = 0;for(int i = 1; i<=q; i++) {if(query(in[p[i].a]) + query(in[p[i].b]) == 0) {ans++;update(in[p[i].lca],1);update(out[p[i].lca]+1,-1);}}printf("%d\n",ans);}return 0 ; }?
總結(jié)
以上是生活随笔為你收集整理的【HDU - 6203】ping ping ping(lca+贪心思想,对lca排序,树状数组差分)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信用卡申请失败多久可以再申请 若因征信问
- 下一篇: 【51nod - 1076】2条不相交的