• HDU 3726 Graph and Queries 平衡树+前向星+并查集+离线操作+逆向思维 数据结构大综合题


    Graph and Queries

    Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

    【Problem Description】
    You are given an undirected graph with N vertexes and M edges. Every vertex in this graph has an integer value assigned to it at the beginning. You're also given a sequence of operations and you need to process them as requested. Here's a list of the possible operations that you might encounter:
    1)  Deletes an edge from the graph. The format is [D X], where X is an integer from 1 to M, indicating the ID of the edge that you should delete. It is guaranteed that no edge will be deleted more than once.
    2)  Queries the weight of the vertex with K-th maximum value among all vertexes currently connected with vertex X (including X itself). The format is [Q X K], where X is an integer from 1 to N, indicating the id of the vertex, and you may assume that K will always fit into a 32-bit signed integer. In case K is illegal, the value for that query will be considered as undefined, and you should return 0 as the answer to that query.
    3)  Changes the weight of a vertex. The format is [C X V], where X is an integer from 1 to N, and V is an integer within the range [-106, 106].

    The operations end with one single character, E, which indicates that the current case has ended. For simplicity, you only need to output one real number - the average answer of all queries.
     
    【Input】
    There are multiple test cases in the input file. Each case starts with two integers N and M (1 <= N <= 2 * 104, 0 <= M <= 6 * 104), the number of vertexes in the graph. The next N lines describes the initial weight of each vertex (-106 <= weight[i] <= 106). The next part of each test case describes the edges in the graph at the beginning. Vertexes are numbered from 1 to N. The last part of each test case describes the operations to be performed on the graph. It is guaranteed that the number of query operations [Q X K] in each case will be in the range [1, 2 * 105], and there will be no more than 2 * 105 operations that change the values of the vertexes [C X V].
    There will be a blank line between two successive cases. A case with N = 0, M = 0 indicates the end of the input file and this case should not be processed by your program.
     
    【Output】
    For each test case, output one real number – the average answer of all queries, in the format as indicated in the sample output. Please note that the result is rounded to six decimal places.
     
    【Sample Input】
    3 3
    10
    20
    30
    1 2
    2 3
    1 3
    D 3
    Q 1 2
    Q 2 1
    D 2
    Q 3 2
    C 1 50
    Q 1 1
    E
    
    3 3
    10
    20
    20
    1 2
    2 3
    1 3
    Q 1 1
    Q 1 2
    Q 1 3
    E
    
    0 0

    【Sample Output】

    Case 1: 25.000000 
    Case 2: 16.666667

    【Hint】

    For the first sample:
    D 3 -- deletes the 3rd edge in the graph (the remaining edges are (1, 2) and (2, 3))
    Q 1 2 -- finds the vertex with the second largest value among all vertexes connected with 1. The answer is 20.
    Q 2 1 -- finds the vertex with the largest value among all vertexes connected with 2. The answer is 30.
    D 2 -- deletes the 2nd edge in the graph (the only edge left after this operation is (1, 2))
    Q 3 2 -- finds the vertex with the second largest value among all vertexes connected with 3.  The answer is 0 (Undefined).
    C 1 50 -- changes the value of vertex 1 to 50.
    Q 1 1 -- finds the vertex with the largest value among all vertex connected with 1. The answer is 50.
    E -- This is the end of the current test case. Four queries have been evaluated, and the answer to this case is (20 + 30 + 0 + 50) / 4 = 25.000.

    For the second sample, caution about the vertex with same weight:
    Q 1 1 – the answer is 20
    Q 1 2 – the answer is 20
    Q 1 3 – the answer is 10

    【题意】

    给出一张无向图,并在图中进行多种操作:

    1.删除一条边;2.改变一个点的权值;3.询问x能够到达的所有点中,第k大的是多少

    【分析】

    花费了好多时间在这道题上,算是这段时间中做到的最综合的数据结构题了。

    首先本题的无向图一开始就是个陷阱,如果单纯地从图的角度来考虑,每次询问都需要遍历全图来找第k大的值,这显然是不可取的,而中间又存在删边操作,图的连通性不是稳定的,结点的权值会变,而且可能多次改变,所以整个图是完全不稳定的,没办法用图论的方法来解决。

    考虑倒过来操作,如果我们从做完所有操作之后最后的结果图出发,逆序回去,则原本的删边可以看成是将两个连通块连接到一起,询问第k值是在x点当前所属的连通块中进行,对点权的修改也是,而对于每一个独立的连通块,最后这两步可以用平衡树来实现。

    所以算法的雏形就有了,询问连通块k值、修改连通块中点的点权操作——平衡树,维护点的连通性——并查集,保存点权的修改信息——前向星

    完整过程:

    1.首先从完整图出发,读入所有操作,记录删掉的边,按顺序记录点权的变化情况,记录其他信息;

    2.用删边信息建立终图的连通性,并查集维护,对于每一个独立的连通块,建立一个独立的平衡树(这里我用的是SBT,网上题解搜出来好多人用的Splay,我其实有点不太理解,感觉这里没有需要用到Splay特殊结构的地方,单纯的维护平衡树的话Splay的稳定性和效率应该是不如SBT的。有大神路过看到这个的话,希望能交流下~~~);

    3.从最后一条操作开始逆序回来:

         i.  询问,则在x所属的平衡树中找第k值;

         ii. 修改,则在x所属的平衡树中删掉原始的值,插入新值,这里对点权的顺序维护我用了前向星,要保证点权的操作也是要有序的;

         iii.删边,在这里就是如果两个点属于两个不同的连通块,则将两个连通块连起来,并查集合并,同时平衡树合并。平衡树合并的时候只能把小的那棵树一个一个加到大的树中去,貌似Splay有个启发式合并,用了finger search神马的东西,可以把合并在O(nlogn)内完成,不会写,ORZ。

    【后记】

    写这道题的时候,SBT模板改了两次,-_-///,然后中间有SBT结合并查集结合前向星的,代码里面就是数组套数组套数组套数组......好多地方写着写着就写乱了,教训是如果能简单,一定不要往复杂了写。

    然后并查集的教训:father[]绝对不能直接引用,必须调用getfather()

      1 /* ***********************************************
      2 MYID    : Chen Fan
      3 LANG    : G++
      4 PROG    : HDU3726
      5 ************************************************ */
      6 
      7 #include <iostream>
      8 #include <cstdio>
      9 #include <cstring>
     10 #include <algorithm>
     11 
     12 using namespace std;
     13 
     14 #define MAXN 20010
     15 
     16 typedef struct enod
     17 {
     18     int p,q;
     19     bool enable;
     20 } enode;
     21 enode e[60010];
     22 
     23 typedef struct qnod
     24 {
     25     int x,k;
     26 } qnode;
     27 qnode lisq[200010];
     28 
     29 typedef struct nod
     30 {
     31     int no,value,time;
     32 } node;
     33 node lis[300010];
     34 
     35 int sons[MAXN][2],size[MAXN],data[MAXN],sbt[MAXN],sbttail;
     36 int lisd[60010],taild,tailq,tail,tailtot,lisc[200010],tailc=0;
     37 int start[MAXN],num[MAXN],father[MAXN];
     38 char listot[500010];
     39 
     40 void rotate(int &t,int w) //rotate(&node,0/1)
     41 {
     42     int k=sons[t][1-w];
     43     if (!k) return ;
     44     sons[t][1-w]=sons[k][w];
     45     sons[k][w]=t;
     46     size[k]=size[t];
     47     size[t]=size[sons[t][0]]+size[sons[t][1]]+1;
     48     t=k;
     49 }
     50 
     51 void maintain(int& t,bool flag) //maintain(&node,flag)
     52 {
     53     if (!t) return ;
     54     if (!flag)
     55         if (size[sons[sons[t][0]][0]]>size[sons[t][1]]) rotate(t,1);
     56         else if (size[sons[sons[t][0]][1]]>size[sons[t][1]]) 
     57         {
     58             rotate(sons[t][0],0);
     59             rotate(t,1);
     60         } else return ;
     61     else
     62         if (size[sons[sons[t][1]][1]]>size[sons[t][0]]) rotate(t,0);
     63         else if (size[sons[sons[t][1]][0]]>size[sons[t][0]])
     64         {
     65             rotate(sons[t][1],1);
     66             rotate(t,0);
     67         } else return ;
     68     
     69     maintain(sons[t][0],false);
     70     maintain(sons[t][1],true);
     71     maintain(t,false);
     72     maintain(t,true);
     73 }
     74 
     75 void insert(int& t,int v,int pos) //insert(&root,value)
     76 {
     77     if (!size[t])
     78     {
     79         if (!pos)
     80         {
     81             sbttail++;
     82             pos=sbttail;
     83         }
     84         data[pos]=v;
     85         size[pos]=1;
     86         sons[pos][0]=0;
     87         sons[pos][1]=0;
     88         t=pos;
     89     } else 
     90     {
     91         size[t]++;
     92         if (v<data[t]) insert(sons[t][0],v,pos);
     93         else insert(sons[t][1],v,pos);
     94         maintain(t,v>=data[t]);
     95     }
     96 }
     97 
     98 int last;
     99 int del(int& t,int v) //node=del(&root,key)
    100 {
    101     size[t]--;
    102     if (v==data[t]||(v<data[t]&&sons[t][0]==0)||(v>data[t]&&sons[t][1]==0))
    103     {
    104         int ret=data[t];
    105         if (sons[t][0]==0||sons[t][1]==0) 
    106         {
    107             last=t;
    108             t=sons[t][1]+sons[t][0];
    109         }
    110         else data[t]=del(sons[t][0],data[t]+1);
    111         return ret;
    112     } else
    113     if (v<data[t]) return del(sons[t][0],v);
    114     else return del(sons[t][1],v);
    115 }
    116 
    117 int select(int t,int k)
    118 {
    119     if (k==size[sons[t][0]]+1) return t;
    120     if (k<=size[sons[t][0]]) return select(sons[t][0],k);
    121     else return select(sons[t][1],k-1-size[sons[t][0]]);
    122 }
    123 
    124 void clean_father(int n)
    125 {
    126     for (int i=1;i<=n;i++)
    127     {
    128         father[i]=i;
    129         sbt[i]=i;
    130     }
    131 } 
    132 
    133 int getfather(int x)
    134 {
    135     if (father[x]!=x) father[x]=getfather(father[x]);
    136     return father[x];
    137 }
    138 
    139 void link(int x,int y)
    140 {
    141     int xx=getfather(x),yy=getfather(y);
    142     if (size[sbt[xx]]>size[sbt[yy]]) 
    143     {
    144         father[yy]=xx;
    145         while(size[sbt[yy]]>0) 
    146         {
    147             int temp=del(sbt[yy],data[sbt[yy]]);
    148             insert(sbt[xx],temp,last);    
    149         }
    150     }
    151     else 
    152     {
    153         father[xx]=yy;
    154         while(size[sbt[xx]]>0) 
    155         {
    156             int temp=del(sbt[xx],data[sbt[xx]]);
    157             insert(sbt[yy],temp,last);
    158         }
    159     }
    160 }
    161 
    162 bool op(node a,node b)
    163 {
    164     if (a.no==b.no) return a.time<b.time;
    165     else return a.no<b.no;
    166 }
    167 
    168 int main()
    169 {
    170     freopen("3726.txt","r",stdin);
    171     
    172     int n,m,tt=0;
    173     while(scanf("%d%d",&n,&m)==2&&(n+m>0))
    174     {
    175         for (int i=1;i<=n;i++)
    176         {
    177             lis[i].no=i;
    178             lis[i].time=i;
    179             scanf("%d",&lis[i].value);
    180         }
    181         for (int i=1;i<=m;i++)
    182         {
    183             scanf("%d%d",&e[i].p,&e[i].q);
    184             e[i].enable=true;
    185         }
    186         taild=0;tailq=0;tailc=0;tail=n;tailtot=0;
    187         bool doit=true;
    188         while(doit)
    189         {
    190             char c=getchar();
    191             while(c!='D'&&c!='Q'&&c!='C'&&c!='E') c=getchar();
    192             tailtot++;
    193             listot[tailtot]=c;
    194             switch(c)
    195             {
    196                 case 'D':
    197                     taild++;
    198                     scanf("%d",&lisd[taild]);
    199                     e[lisd[taild]].enable=false;
    200                     break;
    201                 case 'Q':
    202                     tailq++;
    203                     scanf("%d%d",&lisq[tailq].x,&lisq[tailq].k);
    204                     break;
    205                 case 'C':
    206                     tail++;
    207                     lis[tail].time=tail;
    208                     scanf("%d%d",&lis[tail].no,&lis[tail].value);
    209                     tailc++;
    210                     lisc[tailc]=lis[tail].no;
    211                     break;
    212                 default:
    213                     doit=false;
    214             }
    215         }
    216 
    217         sort(&lis[1],&lis[tail+1],op);
    218         int o=0;
    219         memset(num,0,sizeof(num));
    220         for (int i=1;i<=tail;i++)
    221         {
    222             if (o!=lis[i].no)
    223             {
    224                 o=lis[i].no;
    225                 start[o]=i;
    226             }
    227             num[o]++;
    228         }
    229 
    230         clean_father(n);
    231         sbttail=0;
    232         memset(size,0,sizeof(size));
    233         for (int i=1;i<=n;i++) insert(sbt[i],lis[start[i]+num[i]-1].value,0);
    234         for (int i=1;i<=m;i++)
    235         if (e[i].enable)
    236         if (getfather(e[i].p)!=getfather(e[i].q)) 
    237         link(e[i].p,e[i].q);
    238 
    239         int ansq=tailq;
    240         double ans=0;
    241         for (int i=tailtot-1;i>=1;i--)
    242         switch(listot[i])
    243         {
    244             case 'Q':
    245                 if (lisq[tailq].k>0&&size[sbt[getfather(lisq[tailq].x)]]>=lisq[tailq].k) 
    246                     ans+=data[select(sbt[getfather(lisq[tailq].x)],size[sbt[getfather(lisq[tailq].x)]]-lisq[tailq].k+1)];
    247                 tailq--;
    248                 break;
    249             case 'D':
    250                 if (getfather(e[lisd[taild]].p)!=getfather(e[lisd[taild]].q))
    251                     link(e[lisd[taild]].p,e[lisd[taild]].q);
    252                 taild--;
    253                 break;
    254             case 'C':
    255                 num[lisc[tailc]]--;
    256                 del(sbt[getfather(lisc[tailc])],lis[start[lisc[tailc]]+num[lisc[tailc]]].value);
    257                 insert(sbt[getfather(lisc[tailc])],lis[start[lisc[tailc]]+num[lisc[tailc]]-1].value,last);
    258                 tailc--;
    259         }
    260         tt++;
    261         printf("Case %d: %.6f
    ",tt,ans/ansq);
    262     }
    263 
    264     return 0;
    265 }
    View Code
  • 相关阅读:
    Scrapy(爬虫)基本运行机制
    Python面向对象中的“私有化”
    基于Requests和BeautifulSoup实现“自动登录”
    BeautifulSoup模块
    Python爬虫之-Requests
    踩坑之jinja2注释问题(Flask中)
    Struts2 从一个Action跳至另一个Action
    @JSON(serialize=false),过滤不需要的变量
    JS function的参数问题
    nginx反向代理配置及优化
  • 原文地址:https://www.cnblogs.com/jcf94/p/4360785.html
Copyright © 2020-2023  润新知