HDU - 6203 ping ping ping(LCA+dfs序+线段树)
題目鏈接:點擊查看
題目大意:給出一棵由n+1個節點組成的數,節點編號為0~n(這個無關緊要,預處理時所有節點以及n都自增1即可轉換為正常的模型了),現在給出m組節點u和v,問若想讓m組u和v都斷開連接,則最少需要去掉幾個點
題目分析:首先我們知道,若想讓u和v斷開連接,最優解肯定是切斷其lca,畢竟是最近公共祖先嘛,當然切斷u和切斷v也可以滿足斷開連接,但并不是最優解,因為切斷lca之后,lca下面的所有不同鏈上的節點都不可能互相到達了,相當于將包含lca在內的子樹從主樹上拿了下來,這樣可以達到切斷一個點,既能滿足u和v斷開連接,也能讓最多的點互不相連
不過這就面臨著一個問題,若切斷了當前的lca,下一組u和v的lca比當前lca的深度要深,那該怎么判斷呢,也就是說下一組的u和v雖然位于當前lca的子樹中,但卻并不受其影響,因為他們的lca比當前的lca深度還要深
這時候我們就可以根據每組u和v的lca的深度進行排序了,優先處理深度大的lca肯定是最優解,因為深度越大的lca所包含的子樹就越小,當處理完深度較大的lca后,用線段樹將其子樹標記一下,在進行后續操作的時候,若u或v中至少有一點被標記過了,說明了被標記的點所在的子樹已經與主樹脫離了,也就是說不需要斷掉任何點就已經無法相互連接了(因為前面斷掉的lca充當了當前u與v的連接點),若u與v都沒有被標記,那么直接斷掉其lca即可,也是有一點貪心的策略
感嘆一下吧,這是上樹一來第一道從有思路開始到實現為止一路通順的題目,一口氣寫完代碼,一遍過樣例,最后1A,還是有點小開心的
這個題的線段樹我看網上很多大佬用樹狀數組代替的,我感覺區別不大,主要是我不會樹狀數組。。所以用線段樹做也是沒什么問題的
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e4+100;int n,limit;vector<int>node[N];int deep[N],dp[N][20],L[N],R[N],cnt;//樹上倍增用void dfs(int u,int f,int dep) {L[u]=++cnt;//記錄一下dfs序deep[u]=dep;dp[u][0]=f;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(auto v:node[u]){if(v==f)continue;dfs(v,u,dep+1);}R[u]=cnt; }int LCA(int a,int b)//求LCA {if(deep[a]<deep[b])swap(a,b);for(int i=limit;i>=0;i--)if(deep[a]-deep[b]>=(1<<i))a=dp[a][i];if(a==b)return a;for(int i=limit;i>=0;i--)if(dp[a][i]!=dp[b][i]){a=dp[a][i];b=dp[b][i];}return dp[a][0]; }struct Node {int u,v,lca;bool operator<(const Node& a)const{return deep[lca]>deep[a.lca];} }a[N*5];struct tree {int l,r;bool lazy; }tree[N<<2];//線段樹維護一個lazy標記即可void pushdown(int k) {tree[k<<1].lazy=tree[k<<1|1].lazy=true;tree[k].lazy=false; }void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].lazy=false;if(l==r)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void update(int k,int l,int r) {if(tree[k].r<l||tree[k].l>r)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].lazy=true;return;}if(tree[k].lazy)pushdown(k);update(k<<1,l,r);update(k<<1|1,l,r); }bool query(int k,int pos) {if(tree[k].l==tree[k].r)return tree[k].lazy;if(tree[k].lazy)pushdown(k);int mid=tree[k].l+tree[k].r>>1;if(mid>=pos)return query(k<<1,pos);elsereturn query(k<<1|1,pos); }void init() {for(int i=1;i<=n;i++)node[i].clear();limit=log2(n)+1;cnt=0; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);while(scanf("%d",&n)!=EOF){n++;init();for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);u++,v++;node[u].push_back(v);node[v].push_back(u);}dfs(1,0,0);build(1,1,n);int m;scanf("%d",&m);for(int i=1;i<=m;i++){scanf("%d%d",&a[i].u,&a[i].v);a[i].u++,a[i].v++;a[i].lca=LCA(a[i].u,a[i].v);}sort(a+1,a+1+m);int ans=0;for(int i=1;i<=m;i++){if(query(1,L[a[i].u])||query(1,L[a[i].v]))//如果u和v至少有一個被標記過了,說明已經互不相連了continue;else//若都沒被標記過,斷掉其lca{ans++;update(1,L[a[i].lca],R[a[i].lca]);}}printf("%d\n",ans);}return 0; }?
總結
以上是生活随笔為你收集整理的HDU - 6203 ping ping ping(LCA+dfs序+线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 3360 National
- 下一篇: HDU - 5452 Minimum C