您的位置:首页 > 其它

整体二分(51nod 雪之国度)

2017-04-26 10:11 330 查看
雪之国度有N座城市,依次编号为1到N,又有M条道路连接了其中的城市,每一条道路都连接了不同的2个城市,任何两座不同的城市之间可能不止一条道路。

雪之女王赋予了每一座城市不同的能量,其中第i座城市被赋予的能量为Wi。

如果城市u和v之间有一条道路,那么只要此刻雪之女王的能量不小于|Wu-Wv|,这条道路就是安全的。

如果城市u和v之间存在两条没有重复道路的安全路径(其中每一段道路都是安全的),则认为这两座城市之间有着良好的贸易关系。

最近,雪之女王因为情感问题,她的能量产生巨大的波动。为了维持雪之国度的经济贸易,她希望你能帮忙对Q对城市进行调查。

对于第j对城市uj和vj,她希望知道在保证这两座城市之间有着良好贸易关系的前提之下,自己最少需要保持多少的能量。

题意:

每一条边都有边权,然后每次询问2个点u,v,找到最小的值X,使得所有边权小于等于X的边形成的图中, u v是在一个双连通分量内的

tip:

首先考虑建立最小生成树,每次二分边权,加入小于等于这个权值的所有边,然后对所有区间内询问一分为二,一边是再一个双联通分量,那么她真正的答案一定是小于等于当前值的,另一边是不再一个双连通分量里,那么他的真正答案一定是大于等于当前值的。这样就保证了,logm层 每一层总共询问q次就可以了。总复杂度可行

每一层开始的时候就是最开始的最小生成树,(大于当前要求的边权的只加成树,一定不影响结果)一层以内从左到右不断地插入(边权不断最大),减小复杂度

循环走层,用队列存这一层后面的一分为二(当前每个区间都一份为二),

而每次加边,必然会出现环,uv各自到lca的链上所有点都是双连通了,这个时候按照并查集的思想,把她们的fa都给成lca 那么判断的时候只要findroot之后,fa相同就是双联通了,具体:u,v中比较深的结点和他的fa一起findroot 不一样,就变成她的fa的findroot(链接到一起) 然后u等于他的fa 最后相同为止

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
typedef pair<int,int> pii;
const int maxn = 1e6+10;
const int maxm = 5e6+10;
int n,m,Q,w[maxn],pos_edge,fa[maxn],head[maxn],now;
int tot,h[maxn],ct,Troot,root[maxn],cir[maxm],ans[maxm],qid[maxm],fa_strong[maxm];
pii qu[maxn];

struct que{
int l;
int r;
int qul;
int qur;
}q[maxm],qtmp[maxm];

struct inp{
int u,v,cost,ord;
}data[maxm];

bool cmp(inp a, inp b){
return a.cost < b.cost;
}

void init(){
scanf("%d%d%d",&n,&m,&Q);
tot =0;
ct = 1;
for(int i = 1; i <= n ; i++){
scanf("%d",&w[i]);
head[i] = -1;
root[i] = i;

}
for(int i = 1; i <= m ; i++){
scanf("%d%d",&data[i].u,&data[i].v);
data[i].cost = max(w[data[i].v]-w[data[i].u],w[data[i].u]-w[data[i].v]);
data[i].ord = i;
}
sort(data+1,data+m+1,cmp);

for(int i = 1; i <= Q; i++){
scanf("%d%d",&qu[i].first,&qu[i].second);
qid[i] = i;
ans[i] = (1<<30);
}

}

int findroot(int x){
return root[x] = (x == root[x])?x:findroot(root[x]);
}

struct node{
int u,v,next;
}edges[maxm];

void add(int u,int v){
edges[tot].v =v;edges[tot].next = head[u];head[u] = tot++;
edges[tot].v =u;edges[tot].next = head[v];head[v] = tot++;
}

void dfs(int x,int root,int step){
h[x] = step;
// cout <<"x - "<<x<<endl;
for(int k = head[x] ; k != -1 ; k = edges[k].next){
int v = edges[k].v;
if(v == root) continue;
fa[v] = x;
dfs(v,x,step+1);
}
}

void build_tree(){
Troot = 0;
for(int i = 1 ; i <= m ; i++){
int x = data[i].u,y = data[i].v;
// cout << "u = "<<data[i].u <<"  v = "<<data[i].v<<endl;
int fx = findroot(x),fy = findroot(y);
if(fx != fy){
root[fx] = fy;
//cout <<"x = "<<x<<"  y = "<<y<<endl;
add(x,y);
if(!Troot)  Troot = x;
}
else{
cir[ct++] = i;
}
}
dfs(Troot,-1,1);

}
int find_cir(int x) {
return fa_strong[x] = (fa_strong[x] == x ? x : find_cir(fa_strong[x])) ;
}

void union_cir(int x , int y) {
int fx = find_cir(x) ;
int fy = find_cir(y) ;
fa_strong[fx] = fy ;
//printf("fa_see[%d] = %d\n",fx,fy);
}

int findq(int mid,int ql,int qr){
//cout <<"mid = "<<data[mid].cost<<endl;
for(int i = now ; i < ct ;i++){
int u = data[cir[i]].u,v = data[cir[i]].v;
if(data[cir[i]].cost > data[cir[mid]].cost){
now = i;
break;
}
// cout <<" u  = "<< u <<"  h = "<<h[u] <<"  v = "<<v<<"  h  = "<<h[v]<<endl;
u= find_cir(u);v = find_cir(v);
while(u != v) {
if ( h[u] >= h[v] ) {
union_cir(u , fa[u]) ;
u = find_cir(fa[u]) ;
}
else {
union_cir(v , fa[v]) ;
v = find_cir(fa[v]) ;
}
}
}
//    for(int i = 1 ; i <= n ;i++){
//        //find_cir(i);
//        printf("fa_st[%d] = %d\n",i,fa_strong[i]);
//    }
int bound = ql-1;
//  cout <<"ql = " <<ql <<"   qr = "<<qr<<endl;
for(int L = ql , R =  qr; L <= R;){
// printf("find_cir(%d) = %d\n",qu[L].first,find_cir(qu[L].first));
//printf("find_cir(%d) = %d\n",qu[L].second,find_cir(qu[L].second));

while(L <= R && find_cir(qu[L].first) == find_cir(qu[L].second) ){
//cout <<"qid = "<<qid[L]<<"  u = "<<qu[L].first<<"  v = "<<qu[L].second<<endl;
bound = L;
ans[qid[L]] = min(ans[qid[L]],data[cir[mid]].cost);
// cout << "ans = "<<ans[qid[L]]<<endl;
L++;
}
while(L <= R&& find_cir(qu[R].first) != find_cir(qu[R].second) ){
// printf(" L=  %d  R = %d\n",L,R);
R--;
}

if(L < R){
swap(qu[L],qu[R]);
swap(qid[L],qid[R]);
// cout <<"qid = "<<qid[L]<<"  u = "<<qu[L].first<<"  v = "<<qu[L].second<<endl;
ans[qid[L]] = min(ans[qid[L]],data[cir[mid]].cost);
// cout << "ans ["<<qid[L]<<"] = "<<ans[qid[L]]<<endl;
bound = L;
L++;R--;
}
}
// cout<<" bon  = "<<bound <<endl;
return bound;
}

void sov(){
int lm = 1,rm = ct-1;
int top = 0,tail = -1;
q[++tail].l = lm;
q[tail].r = rm;
q[tail].qul = 1;
q[tail].qur = Q;

for(int i = 1; i <= log2(m) + 1;i++){//O(20)
pos_edge = 0;now = 1;
//cout <<" the cemh = "<< i<<endl;
for(int i = 1; i <= n ; i++)    fa_strong[i] = i;
int numq = 0;
while(top <= tail){
int tmp = (q[top].l+q[top].r)/2;
// printf("bound = %d   qul = %d   qr = %d\n",tmp,q[top].qul,q[top].qur);
int bound = findq(tmp,q[top].qul,q[top].qur);

if(q[top].l <= tmp-1 && q[top].qul <= bound){
qtmp[numq].l =q[top].l;
qtmp[numq].r = tmp-1;
qtmp[numq].qul = q[top].qul;
qtmp[numq++].qur = bound;
}
if(tmp+1 <= q[top].r && bound +1 <= q[top].qur){
qtmp[numq].l =tmp+1;
qtmp[numq].r =q[top].r;
qtmp[numq].qul=bound+1;
qtmp[numq++].qur= q[top].qur;
}
top++;
}

tail = -1;top = 0;
for(int i = 0 ; i< numq ;i++)
q[++tail] = qtmp[i];
}
}

void print_tree(){
for(int i = 1; i <= n ; i++){
printf("fa[%d] = %d\n",i,fa[i]);
}
for(int i = 1 ;i < ct ;i++)
printf("cir[%d] u = %d  v = %d\n",i,data[cir[i]].u,data[cir[i]].v);
}

void print(){
for(int i = 1 ; i <= Q ; i++){
if(ans[i] == (1<<30))  printf("infinitely\n");
else
printf("%d\n",ans[i]);
}
}

int main(){
init();
build_tree();
//print_tree();
sov();
print();
}
/*
7 12 5
3 2 4 1 3 5 9
1 2
1 3
2 4
2 5
3 6
6 7
4 6
5 7
4 5
5 6
3 5
3 7

2 7
2 3
1 4
1 3
1 2

10 10 10
53181 9395 30441 197250 86719 81912 128322 192117 95489 115481
7 1
8 5
2 5
10 8
7 10
9 7
1 5
7 6
5 2
5 2

9 3
5 9
3 10
9 6
4 9
6 8
9 3
9 10
7 8
3 7
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  整体二分