zoukankan      html  css  js  c++  java
  • 牛客/科大讯飞杯 G题血压游戏(虚树 dp)

    https://ac.nowcoder.com/acm/contest/5278/G

    题意很好理解。而且很容易发现树中同一深度的松鼠才会打架。

    预处理出节点的深度和dfs序,然后枚举树的深度,同一深度的所有节点和根节点s去建虚树,每建好一次就从根节点s出发跑一次树型dp

    dp的转移方程比较好想,设当前节点为x,si为x的子树,则dp[x] = ∑max(1,dp[si] + (depth[si] - depth[x]) ) (dp[si]需要>0)

    depth[]表示节点在原树的深度,最终每棵虚树对答案的贡献是 max(1,dp[x] - 1 )(dp[x]>0)

    复杂度O(nlogn)

      1 #include<bits/stdc++.h>
      2 typedef long long ll;
      3 using namespace std;
      4 const int maxbit = 20;
      5 const int maxn = 2e5+5;
      6 vector<int> G[maxn],vt[maxn],p[maxn];//vt为虚树
      7 ll a[maxn],dp[maxn];
      8 int depth[maxn],fa[maxn][maxbit],Log[maxn],in[maxn];//in数组为dfs序
      9 int n,cnt,s;
     10 void add(int u,int v){G[u].push_back(v),G[v].push_back(u);}
     11 bool cmp(int u,int v) {return in[u]<in[v];}
     12 void pre(){
     13     Log[0] = -1;
     14     Log[1] = 0,Log[2] = 1;
     15     for(int i = 3;i<maxn;i++) Log[i] = Log[i/2] + 1;
     16 }
     17 void dfs(int cur,int father){//dfs预处理
     18     in[cur] = ++cnt;//处理dfs序
     19     depth[cur] = depth[father] + 1;//当前结点的深度为父亲结点+1
     20     fa[cur][0] = father;//更新当前结点的父亲结点
     21     for(int j = 1;(1<<j)<=n;j++){//倍增更新当前结点的祖先
     22         fa[cur][j] = fa[fa[cur][j-1]][j-1];
     23     }
     24     for(int i = 0;i<G[cur].size() ;i++){
     25         if(G[cur][i] != father) {//dfs遍历
     26             dfs(G[cur][i],cur);
     27         }
     28     }
     29 }
     30 int LCA(int u,int v){
     31     if(depth[u]<depth[v]) swap(u,v);
     32     int dist = depth[u] - depth[v];//深度差
     33     while(depth[u]!=depth[v]){//把较深的结点u倍增到与v高度相等
     34         u = fa[u][Log[depth[u]-depth[v]]];
     35     }
     36     if(u == v) return u;//如果u倍增到v,说明v是u的LCA
     37     for(int i = Log[depth[u]];i>=0;i--){//否则两者同时向上倍增
     38         if(fa[u][i]!=fa[v][i]){//如果向上倍增的祖先不同,说明是可以继续倍增
     39             u = fa[u][i];//替换两个结点
     40             v = fa[v][i];
     41         }
     42     }
     43     return fa[u][0];//最终结果为u v向上一层就是LCA
     44 }
     45  
     46 void build (int indx){//传虚树和深度为indx的数组
     47     stack<int> st;
     48     st.push(s);//入栈s节点
     49     vt[s].clear();
     50     int tmp ;
     51     for(int i = 0;i<p[indx].size();i++){
     52         tmp = 0;
     53         int cur = LCA(p[indx][i],st.top());//求出栈顶元素和p[i]的LCA
     54         while(!st.empty() && LCA(cur,st.top()) != st.top()){//如果LCA和栈顶元素不一样,建栈中虚树的边
     55             if(tmp) vt[st.top()].push_back(tmp);
     56             tmp = st.top();
     57             st.pop();
     58         }
     59         if(st.empty() || st.top()!=cur){
     60             st.push(cur);//如果栈为空或者栈顶元素不等于LCA,入栈
     61             vt[cur].clear();
     62         }
     63         if(tmp) vt[st.top()].push_back(tmp);//把最后的tmp节点加入链中
     64         st.push(p[indx][i]);//当前p[i]入栈
     65         vt[p[indx][i]].clear();
     66     }
     67     tmp = 0;
     68     while(!st.empty()){//加入最后一条链
     69         if(tmp) vt[st.top()].push_back(tmp);
     70         tmp = st.top();
     71         st.pop();
     72     }
     73 }
     74 void getdp(int cur){//传需要dp的子虚树
     75     dp[cur] = 0;
     76     if(vt[cur].size() == 0) {
     77         dp[cur] = a[cur];
     78         return;
     79     }
     80     for(int i = 0;i<vt[cur].size();i++){
     81         int v = vt[cur][i];
     82         getdp(v);
     83         if(dp[v]!=0){
     84             dp[cur]+=max((ll)1,dp[v]-(depth[v]-depth[cur]));
     85         }
     86     }
     87 }
     88 int main(){
     89     scanf("%d%d",&n,&s);
     90     for(int i = 1;i<=n;i++){
     91         scanf("%lld",&a[i]);
     92     }
     93     for (int i = 0; i < n-1; ++i)
     94     {
     95         int u,v;
     96         scanf("%d%d",&u,&v);
     97         add(u,v);
     98         /* code */
     99     }
    100     pre();
    101     dfs(s,0);
    102     ll ans = 0;
    103     if(a[s]>1) ans+=(a[s]-1);
    104     else ans+=a[s];
    105     for(int i = 1;i<=n;i++) p[depth[i]].push_back(i);
    106     for(int i = 2;i<=n;i++){
    107         sort(p[i].begin(),p[i].end(),cmp);//深度一样的节点按dfs序排序
    108         if(p[i].size() == 0) continue;
    109         build(i);//同一深度的节点建虚树
    110         getdp(s);
    111         if(dp[s]>1) ans+=dp[s]-1;
    112         else ans+=dp[s];
    113         vt[s].clear();
    114         for(int j = 0;j<p[i].size();j++) vt[p[i][j]].clear();
    115     }
    116     printf("%lld",ans);
    117     return 0;
    118 }
  • 相关阅读:
    2020 7 13 每日随笔
    2020 7 10 每日总结
    2020 7 14 每日总结
    2020 7 16 每日总结
    2020 7 15 每日总结
    2020 7 19 每日总结
    2020 7 18 每日总结
    2020 7 17 每日总结
    2020 7 11
    2020 7 12
  • 原文地址:https://www.cnblogs.com/AaronChang/p/12739689.html
Copyright © 2011-2022 走看看