• 【BZOJ 1146】网络管理Network


    Description

    M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门。为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络。该网络的结构由N个路由器和N-1条高速光缆组成。每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络。该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信。 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略。但是由于路由器老化,在这些路由器上进行数据交换会带来很大的延迟。而两个路由器之间的通信延迟时间则与这两个路由器通信路径上所有路由器中最大的交换延迟时间有关。作为M公司网络部门的一名实习员工,现在要求你编写一个简单的程序来监视公司的网络状况。该程序能够随时更新网络状况的变化信息(路由器数据交换延迟时间的变化),并且根据询问给出两个路由器通信路径上延迟第k大的路由器的延迟时间。【任务】 你的程序从输入文件中读入N个路由器和N-1条光缆的连接信息,每个路由器初始的数据交换延迟时间Ti,以及Q条询问(或状态改变)的信息。并依次处理这Q条询问信息,它们可能是: 1. 由于更新了设备,或者设备出现新的故障,使得某个路由器的数据交换延迟时间发生了变化。 2. 查询某两个路由器a和b之间的路径上延迟第k大的路由器的延迟时间。

    Input

    第一行为两个整数N和Q,分别表示路由器总数和询问的总数。第二行有N个整数,第i个数表示编号为i的路由器初始的数据延迟时间Ti。紧接着N-1行,每行包含两个整数x和y。表示有一条光缆连接路由器x和路由器y。紧接着是Q行,每行三个整数k、a、b。如果k=0,则表示路由器a的状态发生了变化,它的数据交换延迟时间由Ta变为b。如果k>0,则表示询问a到b的路径上所经过的所有路由器(包括a和b)中延迟第k大的路由器的延迟时间。注意a可以等于b,此时路径上只有一个路由器。

    Output

    对于每一个第二种询问(k>0),输出一行。包含一个整数为相应的延迟时间。如果路径上的路由器不足k个,则输出信息“invalid request!”(全部小写不包含引号,两个单词之间有一个空格)。

    Sample Input

    5 5
    5 1 2 3 4
    3 1
    2 1
    4 3
    5 3
    2 4 5
    0 1 2
    2 2 3
    2 1 4
    3 3 5

    Sample Output

    3
    2
    2
    invalid request!

    HINT

    10% 测试数据满足N<=8000,Q<=3000,

    40% 测试数据满足所有询问中1<=K<=5 。即路由器的延迟时间不会发生变化。

    100% 测试数据满足N,Q<=80000,任意一个路由器在任何时刻都满足延迟时间小于10^8。对于所有询问满足0<=K<=N 。
     
    分析:
      树上路径第K大带修改,实际上就是BZOJ 2588 + BZOJ 1901,用树状数组套主席树。
      根据DFS序将节点放入树状数组,则每个节点及其子树都对应树状数组上的一段,把树状数组差分来看,一个节点对应的主席树版本就是它的前缀和主席树,每次修改子树,就是修改一段区间[l,r],而差分之后只要修改l和r+1就好了。
      查询的时候,找四个主席树版本(树状数组上的前缀和),节点A、B,和节点A、B的LCA以及LCA的父亲,然后随便搞就好了。
     
    代码:
      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 
      5 #define rint register int
      6 
      7 int n, q, u, v, pow[100000];
      8 int k[100000], a[100000], b[100000];
      9 int begin[100000], end[100000];
     10 int fa[100000][22], depth[100000];
     11 
     12 char ch,B[1<<15],*S=B,*T=B,buf[1<<21],*O=buf,stk[40];
     13 #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
     14 inline const int getint() //输入优化
     15 {
     16     char c = getc();
     17     rint k = 1, r = 0;
     18     for(; c < '0' || c > '9'; c = getc())
     19         if(c == '-') k = -1;
     20     for(; c >= '0' && c <= '9'; c = getc())
     21         r = r * 10 + c - '0';
     22     return k * r;
     23 }
     24 
     25 int lca(rint x, rint y) //找LCA
     26 {
     27     rint i;
     28     if (depth[x] < depth[y])
     29         x ^= y, y ^= x, x ^= y;
     30     for (i = fa[x][21]; depth[x] > depth[y]; i--)
     31         if (depth[fa[x][i]] >= depth[y]) x = fa[x][i];
     32     if (x == y) return x;
     33     for (i = fa[x][21]; i >= 0; i--)
     34         if (fa[x][i] != fa[y][i])
     35             x = fa[x][i], y = fa[y][i];
     36     return fa[x][0];
     37 
     38 }
     39 
     40 /*********edges**********/
     41 int ep[200000], et[200000];
     42 int last[100000], en;
     43 
     44 inline void addedge(rint f, rint t) //邻接表
     45 {
     46     en++;
     47     ep[en] = last[f];
     48     last[f] = en;
     49     et[en] = t;
     50 }
     51 
     52 /*****Discretization*****/
     53 int num[200000], count;
     54 
     55 int lb(rint key) // lower_bound
     56 {
     57     rint l = 1, r = count, mid;
     58     while (l < r)
     59     {
     60         mid = l + r >> 1;
     61         if (num[mid] == key)
     62             return mid;
     63         if (num[mid] < key)
     64             l = mid + 1;
     65         else r = mid - 1;
     66     }
     67     return l;
     68 }
     69 
     70 void discretizate() //离散化
     71 {
     72     std::sort(num + 1, num + count + 1);
     73     rint tmp = 0, i;
     74     num[++count] = 1000000013;
     75     for (i = 1; i <= count; i++)
     76     {
     77         if (num[i] != num[i + 1])
     78             num[++tmp] = num[i];
     79     }
     80     count = tmp;
     81     for (i = 1; i <= n; i++)
     82     {
     83         pow[i] = lb(pow[i]);
     84     }
     85 }
     86 
     87 /********ZX-Tree*********/
     88 int nls[10000000], nrs[10000000], nsum[10000000];
     89 
     90 int ns, tmpos, tmdata;
     91 
     92 void tmodify(int &t, rint left, rint right) //主席树上修改
     93 {
     94     if (t == 0)
     95     {
     96         nsum[t = ++ns] = 0;
     97     }
     98     if (left < right)
     99     {
    100         int mid = left + right >> 1;
    101         if (tmpos <= mid) tmodify(nls[t], left, mid);
    102         else tmodify(nrs[t], mid + 1, right);
    103         nsum[t] += tmdata;
    104     } else nsum[t] += tmdata;
    105 }
    106 
    107 /*******tree-array*******/
    108 int root[100000], rts;
    109 
    110 void modify(rint x, rint y, rint pos, rint data) //树状数组上修改一段区间
    111 {
    112     tmpos = pos, tmdata = data;
    113     for (; x <= n; x += x & -x)
    114         tmodify(root[x], 1, count);
    115     tmdata = -data;
    116     for (y++; y <= n; y += y & -y)
    117         tmodify(root[y], 1, count);
    118 }
    119 
    120 int rs[4][30];
    121 
    122 int init_rs(rint i, rint x) //用rs数组记录查询时需要用到的点
    123 {
    124     memset(rs[i], 0, sizeof (rs[i]));
    125     int ret = 0;
    126     for (; x > 0; x -= x & -x)
    127     {
    128         rs[i][++rs[i][0]] = root[x];
    129         ret += nsum[root[x]];
    130     }
    131     return ret;
    132 }
    133 
    134 int query_rs() //查询每个点右儿子的大小
    135 {
    136     int ret = 0; rint i;
    137     for (i = 1; i <= rs[0][0]; i++)
    138         ret += nsum[nrs[rs[0][i]]];
    139     for (i = 1; i <= rs[1][0]; i++)
    140         ret += nsum[nrs[rs[1][i]]];
    141     for (i = 1; i <= rs[2][0]; i++)
    142         ret -= nsum[nrs[rs[2][i]]];
    143     for (i = 1; i <= rs[3][0]; i++)
    144         ret -= nsum[nrs[rs[3][i]]];
    145     return ret;
    146 }
    147 
    148 void down(rint d) //将每个点变成它的左/右儿子
    149 {
    150     rint x, i;
    151     for (x = 0; x < 4; x++)
    152         for (i = 1; i <= rs[x][0]; i++)
    153             rs[x][i] = d ? nrs[rs[x][i]] : nls[rs[x][i]];
    154 }
    155 
    156 int query(rint x, rint y, rint data) //查询
    157 {
    158     int s = 0, f = lca(x, y), mid;
    159     s += init_rs(0, begin[x]);
    160     s += init_rs(1, begin[y]);
    161     s -= init_rs(2, begin[f]);
    162     s -= init_rs(3, begin[fa[f][0]]);
    163     if (s < data) return -1;
    164     int left = 1, right = count;
    165     while (left < right) //迭代
    166     {
    167         mid = left + right >> 1;
    168         s = query_rs();
    169         if (data > s)
    170         {
    171             down(0);
    172             right = mid;
    173             data -= s;
    174         }
    175         else
    176         {
    177             down(1);
    178             left = mid + 1;
    179         }
    180     }
    181     return num[left];
    182 }
    183 
    184 /**********dfs***********/
    185 void DFS(rint x) //找出dfs序和求lca的rmq数组
    186 {
    187     rint i;
    188     depth[x] = depth[fa[x][0]] + 1;
    189     for (i = 0; fa[x][i]; fa[x][21] = ++i)
    190         fa[x][i + 1] = fa[fa[x][i]][i];
    191     begin[x] = ++rts;
    192     root[begin[x]] = ++ns;
    193     for (i = last[x]; i; i = ep[i])
    194         if (begin[et[i]] == 0)
    195             fa[et[i]][0] = x, DFS(et[i]);
    196     end[x] = rts;
    197 }
    198 
    199 int main()
    200 {
    201     n = getint();
    202     q = getint();
    203     count = 0;
    204     rint i, tmp;
    205     for (i = 1; i <= n; i++)
    206         num[++count] = pow[i] = getint();
    207     for (i = 1; i < n; i++)
    208     {
    209         u = getint();
    210         v = getint();
    211         addedge(u, v);
    212         addedge(v, u);
    213     }
    214     for (i = 1; i <= q; i++)
    215     {
    216         k[i] = getint();
    217         a[i] = getint();
    218         b[i] = getint();
    219         if (k[i] == 0) num[++count] = b[i];
    220     }
    221     discretizate();
    222     DFS(1);
    223     for (i = 1; i <= n; i++)
    224     {
    225         modify(begin[i], end[i], pow[i], 1);
    226     }
    227     for (i = 1; i <= q; i++)
    228     {
    229         if (k[i])
    230         {
    231             tmp = query(a[i], b[i], k[i]);
    232             if (tmp >= 0) printf("%d
    ", tmp);
    233             else printf("invalid request!
    ");
    234         }
    235         else
    236         {
    237             modify(begin[a[i]], end[a[i]], pow[a[i]], -1);
    238             pow[a[i]] = lb(b[i]);
    239             modify(begin[a[i]], end[a[i]], pow[a[i]], 1);
    240         }
    241     }
    242 }

    改进:

      发现还是不够快,研究了一下其它博客上飞快的代码,才知道原来他们把原本的内容和修改的内容拆开,原本的内容用普通的主席树记,和BZOJ 2588一样,修改时候用树状数组,查的时候一起查,这样预处理的负责度就从Nlog^2(N)降到了NlogN。

    代码:

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 
      5 #define rint register int
      6 
      7 int n, q, u, v, pow[100000];
      8 int k[100000], a[100000], b[100000];
      9 int begin[100000], end[100000];
     10 int fa[100000][22], depth[100000];
     11 
     12 char ch,B[1<<15],*S=B,*T=B,buf[1<<21],*O=buf,stk[40];
     13 #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
     14 inline const int getint() //输入优化
     15 {
     16     char c = getc();
     17     rint k = 1, r = 0;
     18     for(; c < '0' || c > '9'; c = getc())
     19         if(c == '-') k = -1;
     20     for(; c >= '0' && c <= '9'; c = getc())
     21         r = r * 10 + c - '0';
     22     return k * r;
     23 }
     24 
     25 int lca(rint x, rint y) //找LCA
     26 {
     27     rint i;
     28     if (depth[x] < depth[y])
     29         x ^= y, y ^= x, x ^= y;
     30     for (i = fa[x][21]; depth[x] > depth[y]; i--)
     31         if (depth[fa[x][i]] >= depth[y]) x = fa[x][i];
     32     if (x == y) return x;
     33     for (i = fa[x][21]; i >= 0; i--)
     34         if (fa[x][i] != fa[y][i])
     35             x = fa[x][i], y = fa[y][i];
     36     return fa[x][0];
     37 
     38 }
     39 
     40 /*********edges**********/
     41 int ep[200000], et[200000];
     42 int last[100000], en;
     43 
     44 inline void addedge(rint f, rint t) //邻接表
     45 {
     46     en++;
     47     ep[en] = last[f];
     48     last[f] = en;
     49     et[en] = t;
     50 }
     51 
     52 /*****Discretization*****/
     53 int num[200000], count;
     54 
     55 int lb(rint key) // lower_bound
     56 {
     57     rint l = 1, r = count, mid;
     58     while (l < r)
     59     {
     60         mid = l + r >> 1;
     61         if (num[mid] == key)
     62             return mid;
     63         if (num[mid] < key)
     64             l = mid + 1;
     65         else r = mid - 1;
     66     }
     67     return l;
     68 }
     69 
     70 void discretizate() //离散化
     71 {
     72     std::sort(num + 1, num + count + 1);
     73     rint tmp = 0, i;
     74     num[++count] = 1000000013;
     75     for (i = 1; i <= count; i++)
     76     {
     77         if (num[i] != num[i + 1])
     78             num[++tmp] = num[i];
     79     }
     80     count = tmp;
     81     for (i = 1; i <= n; i++)
     82     {
     83         pow[i] = lb(pow[i]);
     84     }
     85 }
     86 
     87 /********ZX-Tree*********/
     88 int nls[10000000], nrs[10000000], nsum[10000000];
     89 
     90 int ns, tmpos, tmdata;
     91 
     92 int tmodify(rint left, rint right, rint together) //主席树上修改
     93 {
     94     int i = ++ns;
     95     if (left < right)
     96     {
     97         int mid = left + right >> 1;
     98         if (tmpos <= mid)
     99         {
    100             nls[i] = tmodify(left, mid, nls[together]);
    101             nrs[i] = nrs[together];
    102         }
    103         else
    104         {
    105             nrs[i] = tmodify(mid + 1, right, nrs[together]);
    106             nls[i] = nls[together];
    107         }
    108         nsum[i] = nsum[nls[i]] + nsum[nrs[i]];
    109     } else nsum[i] = nsum[together] + tmdata;
    110     return i;
    111 }
    112 
    113 /*******tree-array*******/
    114 int root[100000], rts, rootofN[100000];
    115 
    116 void modify(rint x, rint y, rint pos, rint data) //树状数组上修改一段区间
    117 {
    118     tmpos = pos, tmdata = data;
    119     for (; x <= n; x += x & -x)
    120         root[x] = tmodify(1, count, root[x]);
    121     tmdata = -data;
    122     for (y++; y <= n; y += y & -y)
    123         root[y] = tmodify(1, count, root[y]);
    124 }
    125 
    126 int rs[4][30];
    127 
    128 int init_rs(rint i, rint x) //用rs数组记录查询时需要用到的点
    129 {
    130     memset(rs[i], 0, sizeof (rs[i]));
    131     rs[i][++rs[i][0]] = rootofN[x];
    132     int ret = nsum[rootofN[x]];
    133     x = begin[x];
    134     for (; x > 0; x -= x & -x)
    135     {
    136         rs[i][++rs[i][0]] = root[x];
    137         ret += nsum[root[x]];
    138     }
    139     return ret;
    140 }
    141 
    142 int query_rs() //查询每个点右儿子的大小
    143 {
    144     int ret = 0; rint i;
    145     for (i = 1; i <= rs[0][0]; i++)
    146         ret += nsum[nrs[rs[0][i]]];
    147     for (i = 1; i <= rs[1][0]; i++)
    148         ret += nsum[nrs[rs[1][i]]];
    149     for (i = 1; i <= rs[2][0]; i++)
    150         ret -= nsum[nrs[rs[2][i]]];
    151     for (i = 1; i <= rs[3][0]; i++)
    152         ret -= nsum[nrs[rs[3][i]]];
    153     return ret;
    154 }
    155 
    156 void down(rint d) //将每个点变成它的左/右儿子
    157 {
    158     rint x, i;
    159     for (x = 0; x < 4; x++)
    160         for (i = 1; i <= rs[x][0]; i++)
    161             rs[x][i] = d ? nrs[rs[x][i]] : nls[rs[x][i]];
    162 }
    163 
    164 int query(rint x, rint y, rint data) //查询
    165 {
    166     int s = 0, f = lca(x, y), mid;
    167     s += init_rs(0, x);
    168     s += init_rs(1, y);
    169     s -= init_rs(2, f);
    170     s -= init_rs(3, fa[f][0]);
    171     if (s < data) return -1;
    172     int left = 1, right = count;
    173     while (left < right) //迭代
    174     {
    175         mid = left + right >> 1;
    176         s = query_rs();
    177         if (data > s)
    178         {
    179             down(0);
    180             right = mid;
    181             data -= s;
    182         }
    183         else
    184         {
    185             down(1);
    186             left = mid + 1;
    187         }
    188     }
    189     return num[left];
    190 }
    191 
    192 /**********dfs***********/
    193 void DFS(rint x) //找出dfs序和求lca的rmq数组
    194 {
    195     rint i;
    196     depth[x] = depth[fa[x][0]] + 1;
    197     for (i = 0; fa[x][i]; fa[x][21] = ++i)
    198         fa[x][i + 1] = fa[fa[x][i]][i];
    199     tmpos = pow[x], tmdata = 1;
    200     rootofN[x] = tmodify(1, count, rootofN[fa[x][0]]);
    201     begin[x] = ++rts;
    202     for (i = last[x]; i; i = ep[i])
    203         if (begin[et[i]] == 0)
    204             fa[et[i]][0] = x, DFS(et[i]);
    205     end[x] = rts;
    206 }
    207 
    208 int main()
    209 {
    210     n = getint();
    211     q = getint();
    212     count = 0;
    213     rint i, tmp;
    214     for (i = 1; i <= n; i++)
    215         num[++count] = pow[i] = getint();
    216     for (i = 1; i < n; i++)
    217     {
    218         u = getint();
    219         v = getint();
    220         addedge(u, v);
    221         addedge(v, u);
    222     }
    223     for (i = 1; i <= q; i++)
    224     {
    225         k[i] = getint();
    226         a[i] = getint();
    227         b[i] = getint();
    228         if (k[i] == 0) num[++count] = b[i];
    229     }
    230     discretizate();
    231     DFS(n / 2 + 1);
    232     for (i = 1; i <= q; i++)
    233     {
    234         if (k[i])
    235         {
    236             tmp = query(a[i], b[i], k[i]);
    237             if (tmp >= 0) printf("%d
    ", tmp);
    238             else printf("invalid request!
    ");
    239         }
    240         else
    241         {
    242             modify(begin[a[i]], end[a[i]], pow[a[i]], -1);
    243             pow[a[i]] = lb(b[i]);
    244             modify(begin[a[i]], end[a[i]], pow[a[i]], 1);
    245         }
    246     }
    247 }

    这道题理论上主席树会有25000000+的节点,实际上不到10000000……

  • 相关阅读:
    js Touch事件(向左滑动,后退)
    浏览器滚动条到底部自动加载更多
    如何终止terminal后台进程
    DFT介绍
    GDS area estimation
    Web Scraping with Python第二章
    Web Scraping with Python第一章
    python技巧汇总
    centos6.4安装zsh与dircolors
    linux解压命令汇总
  • 原文地址:https://www.cnblogs.com/lightning34/p/4615593.html
Copyright © 2020-2023  润新知