uvalive 5031 Graph and Queries 名次树+Treap

题意:给你个点m条边的无向图,每个节点都有一个整数权值。你的任务是执行一系列操作。操作分为3种。。。

思路:本题一点要逆向来做,正向每次如果删边,复杂度太高。逆向到一定顺序的时候添加一条边更容易。详见算法指南P235。

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

 

转载于:https://www.cnblogs.com/ITUPC/p/5074251.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值