zoukankan      html  css  js  c++  java
  • 蓝皮书:异象石 【dfs序+lca】

    题目详见蓝皮书【算法竞赛:进阶指南】。


    题目大意:

    就是给你一颗树,然后我们要在上面进行三种操作: 

     1.标记某个点  或者  2.撤销某个点的标记  以及   3.询问标记点在树上连通所需的最短总边权


    数据范围:

    点数以及操作数:1e5,边权:1e9(意思就是答案要 long long 存)。


    分析:

    这道题比赛的时候看的是真懵逼。。。

    表示只会 n^3 做法(最多会n^2),以及特殊形态(比如链或者菊花图)的骗分法。

    然鹅正解大概是 $O(n log n)$  的做法,和树搭上了关系,加上这数据范围...

    于是正解真是这个复杂度。(看到标程的时候表示惊讶,我太弱了)

    标算就是用的dfs序加上lca的算法(如题)

    首先我们不考虑 2、3 操作,我们先考虑如果树上有 k 个点被标记了,我们要得到树上 k 个点连通图的最小总边权。

    我们可以在纸上画出这棵树以及标记的点,然后我们从左到右把点连成一块。

    这时我们发现每两个相邻的点之间(相当于dfs序)的距离加上最后一个点和第一个点的距离,和正好是答案的两倍。

    也就是说,如果把这些标记点按照dfs序排成一列,首尾相连形成一个环的话,答案就是这个环相邻点距离之和除以二。

    (这种东西考场上怎么做得出来嘛)

    那么我们回到原题,如果用上面的方法暴力处理答案,那么复杂度是 $O(n^{2} log n)$ 的(还不如 我自己想到的 n^2 咧)。

    那么我们结合加点删点特殊的性质,以此优化算法复杂度。

    我们发现加点其实就是在原环两相邻点之间插入了一个新点,然后原来两个相邻点的贡献没了,但是多了新点与这两个点分别的贡献。

    那么删点类似的,就是令原环中某个点与相邻的两个点之间的贡献删除,并且多了这两个点之间的距离的贡献。

    于是我们就可以用一个set来维护标记点,然后每次求距离的时候要用到 lca。

    (lca建议常数小的树剖,倍增效率感人,tarjan 的话比较冷门基本不考虑)

    那么我们就可以愉快地敲代码了!


    代码 :

     1 //by Judge
     2 #include<bits/stdc++.h>
     3 #pragma GCC optimize(2)
     4 #define It set<int>::iterator
     5 #define ll long long
     6 using namespace std;
     7 const int M=1e5+11;
     8 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
     9 char buf[1<<21],*p1,*p2;
    10 inline int read(){ int x=0;
    11     char c=getchar(); while(!isdigit(c)) c=getchar();
    12     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;
    13 } inline int cread(){ char c=getchar();
    14     while(c!='+'&&c!='-'&&c!='?') c=getchar();
    15     return c=='?'?1:(c=='+'?2:3);
    16 } char sr[1<<21],z[20];int C=-1,Z;
    17 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    18 inline void print(ll x,char chr='
    '){
    19     if(C>1<<20)Ot(); while(z[++Z]=x%10+48,x/=10);
    20     while(sr[++C]=z[Z],--Z);sr[++C]=chr;
    21 } int n,m,pat,tim,head[M],f[M],son[M]; ll ans,dis[M];
    22 int siz[M],top[M],dep[M],dfn[M],p[M]; set<int> s;
    23 struct Edge{ int to,val,nxt;
    24     Edge(int v,int c,int x):to(v),val(c),nxt(x){} Edge(){}
    25 }e[M<<1];
    26 inline void add(int u,int v,int c){
    27     e[++pat]=Edge(v,c,head[u]),head[u]=pat;
    28     e[++pat]=Edge(u,c,head[v]),head[v]=pat;
    29 }
    30 #define v e[i].to
    31 void dfs(int u,int fa){
    32     siz[u]=1,f[u]=fa,dep[u]=dep[fa]+1;
    33     for(int i=head[u];i;i=e[i].nxt) if(v^fa){
    34         dis[v]=dis[u]+e[i].val,
    35         dfs(v,u),siz[u]+=siz[v];
    36         if(siz[v]>siz[son[u]]) son[u]=v;
    37     }
    38 } void dfs(int u){
    39     dfn[u]=++tim,p[tim]=u;
    40     if(!top[u]) top[u]=u; if(!son[u]) return ;
    41     top[son[u]]=top[u],dfs(son[u]);
    42     for(int i=head[u];i;i=e[i].nxt)
    43         if(v^f[u]&&v^son[u]) dfs(v);
    44 }
    45 #undef v
    46 inline int lca(int u,int v){
    47     while(top[u]^top[v])
    48         (dep[top[u]]>dep[top[v]])?
    49             u=f[top[u]]:v=f[top[v]];
    50     return dep[u]<dep[v]?u:v;
    51 } inline ll get(int u,int v){
    52     return dis[u]+dis[v]-dis[lca(u,v)]*2;
    53 } inline It L(It it){
    54     return (it==s.begin())?--s.end():--it;
    55 } inline It R(It it){
    56     return (it==--s.end())?s.begin():++it;
    57 }
    58 int main(){
    59     n=read();
    60     for(int i=1,u,v,c;i<n;++i)
    61         u=read(),v=read(),
    62         c=read(),add(u,v,c);
    63     dfs(1,0),dfs(1),m=read();
    64     for(int opt,x,t;m;--m){
    65         opt=cread(); It it;
    66         if(opt==1) print(ans/2);
    67         else if(opt==2){ x=read();
    68             if(s.size()){
    69                 it=s.lower_bound(dfn[x]);
    70                 if(it==s.end()) it=s.begin(); t=*L(it);
    71                 ans+=get(x,p[t])+get(x,p[*it])-get(p[t],p[*it]);
    72             } s.insert(dfn[x]);
    73         } else if(opt==3){ x=read();
    74             if(s.size()>1){
    75                 it=s.find(dfn[x]),t=*L(it),it=R(it);
    76                 ans-=get(x,p[t])+get(x,p[*it])-get(p[t],p[*it]);
    77             } s.erase(dfn[x]);
    78         }
    79     } return Ot(),0;
    80 }
  • 相关阅读:
    powerdesigner添加mysql的字符集ENGINE和DEFAULT CHARACTER SET
    powerdesigner怎么设置同时显示name和code
    更改gradle的java的class文件输出目录的结构
    使用TortoiseGit时如何实现SSH免密码登录
    TortoiseGit之配置密钥
    Mock InjectMocks ( @Mock 和 @InjectMocks )区别
    Centos tomcat jmx 远程连接
    【C++】常见易犯错误之数值类型取值溢出与截断(3)
    【C++】常见易犯错误之数值类型取值溢出与截断(2)
    【C++】常见易犯错误之数值类型取值溢出与截断(1)
  • 原文地址:https://www.cnblogs.com/Judge/p/9882454.html
Copyright © 2011-2022 走看看