//目录

LA 5031 图询问

题目链接:https://vjudge.net/contest/159527#problem/A

题意:(求一个 图 中的连通分量中的 第 k 大)

一张图,n 个点,m 条边,

有一些操作:

删除 ID 为 x 的边,(从 1 到 m);

询问 x 所在的连通分量 里面第 k 大的权值;

把结点 X 的权值 改成 V;

求:

所有的询问后,计算平均值;

 

每个连通分量都是一颗Treap树,加边操作,就是树的合并;

刘汝佳采用的是离线算法,我还是第一次听说,但是还是可以按照题意直接模拟的(我猜,但是很麻烦);因为在Treap中删边不同于删点;

什么是离线算法呢?

把操作顺序反过来处理,执行完所有 删除边操作,然后建Treap,要是不在同一个连通分量里面(并查集判断),这就涉及到递归合并Treap树了,这里采用了启发式合并;

 

然后反向操作,遇到 D,就是加边(加边操作同上),

询问,就是在 X 所在连通分量里面,寻找第 k 大;

改权,就是删除这个点,然后从新加点;

 

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 struct Node
  6 {
  7     Node *ch[2];
  8     int r;  //优先级
  9     int v;  //
 10     int s;  //结点总数
 11 
 12     Node(int v):v(v)
 13     {
 14         ch[0] = ch[1] = NULL;
 15         r = rand();
 16         s = 1;
 17     }
 18 
 19     bool operator < (const Node& rhs) const
 20     {
 21         return r < rhs.r;
 22     }
 23 
 24     int cmp(int x) const
 25     {
 26         if(x==v) return -1;
 27         return x < v ? 0 : 1;
 28     }
 29 
 30     void maintain()
 31     {
 32         s = 1;
 33         if(ch[0]!=NULL) s+=ch[0]->s;
 34         if(ch[1]!=NULL) s+=ch[1]->s;
 35     }
 36 
 37 
 38 };
 39 
 40 void rotate(Node* &o,int d)
 41 {
 42     Node* k = o->ch[d^1];
 43     o->ch[d^1] = k ->ch[d];
 44     k->ch[d] = o;
 45     o->maintain();
 46     k->maintain();
 47     o = k;
 48 }
 49 
 50 void insert(Node* &o,int x)
 51 {
 52     if(o==NULL) o = new Node(x);
 53     else
 54     {
 55         int d = (x < o->v? 0 : 1);
 56         insert(o->ch[d],x);
 57         if(o->ch[d]->r > o->r)
 58             rotate(o,d^1);
 59     }
 60     o->maintain();
 61 }
 62 
 63 void remove(Node* &o,int x)
 64 {
 65     int d = o->cmp(x);
 66     if(d==-1)
 67     {
 68         Node* u = 0;
 69         if(o->ch[0]!=NULL&&o->ch[1]!=NULL)
 70         {
 71             int d2 = (o->ch[0]->r > o->ch[1]->r ? 1 : 0);
 72             rotate(o,d2);
 73             remove(o->ch[d2],x);
 74         }
 75         else
 76         {
 77             if(o->ch[0]==NULL)
 78                 o = o->ch[1];
 79             else o = o->ch[0];
 80         }
 81     }
 82     else
 83         remove(o->ch[d],x);
 84 
 85     if(o!=NULL) o->maintain();
 86 }
 87 
 88 
 89 const int maxc = 500000 + 10;
 90 struct Command
 91 {
 92     char type;
 93     int x,p;
 94 } commands[maxc];
 95 
 96 const int maxn = 20000 + 10;
 97 const int maxm = 60000 + 10;
 98 int n,m;
 99 int weight[maxn],from[maxm],to[maxm],removed[maxm];
100 
101 
102 int pa[maxn];
103 int findset(int x)
104 {
105     return pa[x]!=x ? pa[x] = findset(pa[x]):x;
106 }
107 
108 Node* root[maxn];   //Treap
109 
110 int kth(Node* o,int k)
111 {
112     if(o==NULL||k<=0||k> o->s) return 0;
113     int s = (o->ch[1]==NULL?0:o->ch[1]->s);
114     if(k==s+1) return o->v;
115     else if(k<=s) return kth(o->ch[1],k);
116     else return kth(o->ch[0],k-s-1);
117 }
118 
119 void mergeto(Node* &src,Node* &dest)
120 {
121     if(src->ch[0]!=NULL) mergeto(src->ch[0],dest);
122     if(src->ch[1]!=NULL) mergeto(src->ch[1],dest);
123     insert(dest,src->v);
124     delete src;
125     src = NULL;
126 }
127 
128 void removetree(Node* &x)
129 {
130     if(x->ch[0]!=NULL) removetree(x->ch[0]);
131     if(x->ch[1]!=NULL) removetree(x->ch[1]);
132     delete x;
133     x = NULL;
134 }
135 
136 void add_edge(int x)
137 {
138     int u = findset(from[x]),v=findset(to[x]);
139     if(u!=v)
140     {
141         if(root[u]->s < root[v]->s)
142         {
143             pa[u] = v;
144             mergeto(root[u],root[v]);
145         }
146         else
147         {
148             pa[v] = u;
149             mergeto(root[v],root[u]);
150         }
151     }
152 }
153 
154 int query_cnt;
155 long long query_tot;
156 void query(int x,int k)
157 {
158     query_cnt++;
159     query_tot +=kth(root[findset(x)],k);
160 }
161 
162 void change_weight(int x,int v)
163 {
164     int u = findset(x);
165     remove(root[u],weight[x]);
166     insert(root[u],v);
167     weight[x] = v;
168 }
169 
170 
171 int main()
172 {
173     int kase = 0;
174     while(scanf("%d%d",&n,&m)==2&&n)
175     {
176         for(int i=1; i<=n; i++)
177             scanf("%d",&weight[i]);
178         for(int i=1; i<=m; i++)
179             scanf("%d%d",&from[i],&to[i]);
180         memset(removed,0,sizeof(removed));
181 
182         int c = 0;
183         for(;;)
184         {
185             char type;
186             int x,p=0,v = 0;
187             scanf(" %c",&type);
188             if(type=='E') break;
189             scanf("%d",&x);
190             if(type=='D') removed[x]= 1;    //删掉的边
191             if(type=='Q') scanf("%d",&p);
192             if(type=='C')
193             {
194                 scanf("%d",&v);
195                 p = weight[x];
196                 weight[x] = v;
197             }
198             commands[c++] = (Command)
199             {
200                 type,x,p
201             };
202         }
203 
204         //最终的图
205         for(int i=1; i<=n; i++)
206         {
207             pa[i] = i;
208             if(root[i]!=NULL) removetree(root[i]);
209             root[i] = new Node(weight[i]);
210         }
211         for(int i=1; i<=m; i++)
212         {
213             if(!removed[i]) //id为i这条边没有被删掉
214                 add_edge(i);
215         }
216 
217         query_cnt = query_tot = 0;
218         for(int i=c-1; i>=0; i--)
219         {
220             if(commands[i].type=='D') add_edge(commands[i].x);  //加上边
221             if(commands[i].type=='Q') query(commands[i].x,commands[i].p);//第p大
222             if(commands[i].type=='C') change_weight(commands[i].x,commands[i].p);
223         }
224 
225         printf("Case %d: %.6lf\n",++kase,query_tot/(double)query_cnt);
226 
227     }
228 
229 
230 
231     return 0;
232 }
View Code

 

posted @ 2017-04-19 20:57  小草的大树梦  阅读(329)  评论(0编辑  收藏  举报