HDU 3726 Graph and Queries 平衡树+前向星+并查集+离线操作+逆向思维 数据结构大综合题
Graph and Queries
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
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.
D
Q
Q
D
Q
C
Q
E Q
Q
Q
E
【Sample Output】
Case : 25.000000
Case : 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()
/* ***********************************************
MYID : Chen Fan
LANG : G++
PROG : HDU3726
************************************************ */ #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm> using namespace std; #define MAXN 20010 typedef struct enod
{
int p,q;
bool enable;
} enode;
enode e[]; typedef struct qnod
{
int x,k;
} qnode;
qnode lisq[]; typedef struct nod
{
int no,value,time;
} node;
node lis[]; int sons[MAXN][],size[MAXN],data[MAXN],sbt[MAXN],sbttail;
int lisd[],taild,tailq,tail,tailtot,lisc[],tailc=;
int start[MAXN],num[MAXN],father[MAXN];
char listot[]; void rotate(int &t,int w) //rotate(&node,0/1)
{
int k=sons[t][-w];
if (!k) return ;
sons[t][-w]=sons[k][w];
sons[k][w]=t;
size[k]=size[t];
size[t]=size[sons[t][]]+size[sons[t][]]+;
t=k;
} void maintain(int& t,bool flag) //maintain(&node,flag)
{
if (!t) return ;
if (!flag)
if (size[sons[sons[t][]][]]>size[sons[t][]]) rotate(t,);
else if (size[sons[sons[t][]][]]>size[sons[t][]])
{
rotate(sons[t][],);
rotate(t,);
} else return ;
else
if (size[sons[sons[t][]][]]>size[sons[t][]]) rotate(t,);
else if (size[sons[sons[t][]][]]>size[sons[t][]])
{
rotate(sons[t][],);
rotate(t,);
} else return ; maintain(sons[t][],false);
maintain(sons[t][],true);
maintain(t,false);
maintain(t,true);
} void insert(int& t,int v,int pos) //insert(&root,value)
{
if (!size[t])
{
if (!pos)
{
sbttail++;
pos=sbttail;
}
data[pos]=v;
size[pos]=;
sons[pos][]=;
sons[pos][]=;
t=pos;
} else
{
size[t]++;
if (v<data[t]) insert(sons[t][],v,pos);
else insert(sons[t][],v,pos);
maintain(t,v>=data[t]);
}
} int last;
int del(int& t,int v) //node=del(&root,key)
{
size[t]--;
if (v==data[t]||(v<data[t]&&sons[t][]==)||(v>data[t]&&sons[t][]==))
{
int ret=data[t];
if (sons[t][]==||sons[t][]==)
{
last=t;
t=sons[t][]+sons[t][];
}
else data[t]=del(sons[t][],data[t]+);
return ret;
} else
if (v<data[t]) return del(sons[t][],v);
else return del(sons[t][],v);
} int select(int t,int k)
{
if (k==size[sons[t][]]+) return t;
if (k<=size[sons[t][]]) return select(sons[t][],k);
else return select(sons[t][],k--size[sons[t][]]);
} void clean_father(int n)
{
for (int i=;i<=n;i++)
{
father[i]=i;
sbt[i]=i;
}
} int getfather(int x)
{
if (father[x]!=x) father[x]=getfather(father[x]);
return father[x];
} void link(int x,int y)
{
int xx=getfather(x),yy=getfather(y);
if (size[sbt[xx]]>size[sbt[yy]])
{
father[yy]=xx;
while(size[sbt[yy]]>)
{
int temp=del(sbt[yy],data[sbt[yy]]);
insert(sbt[xx],temp,last);
}
}
else
{
father[xx]=yy;
while(size[sbt[xx]]>)
{
int temp=del(sbt[xx],data[sbt[xx]]);
insert(sbt[yy],temp,last);
}
}
} bool op(node a,node b)
{
if (a.no==b.no) return a.time<b.time;
else return a.no<b.no;
} int main()
{
freopen("3726.txt","r",stdin); int n,m,tt=;
while(scanf("%d%d",&n,&m)==&&(n+m>))
{
for (int i=;i<=n;i++)
{
lis[i].no=i;
lis[i].time=i;
scanf("%d",&lis[i].value);
}
for (int i=;i<=m;i++)
{
scanf("%d%d",&e[i].p,&e[i].q);
e[i].enable=true;
}
taild=;tailq=;tailc=;tail=n;tailtot=;
bool doit=true;
while(doit)
{
char c=getchar();
while(c!='D'&&c!='Q'&&c!='C'&&c!='E') c=getchar();
tailtot++;
listot[tailtot]=c;
switch(c)
{
case 'D':
taild++;
scanf("%d",&lisd[taild]);
e[lisd[taild]].enable=false;
break;
case 'Q':
tailq++;
scanf("%d%d",&lisq[tailq].x,&lisq[tailq].k);
break;
case 'C':
tail++;
lis[tail].time=tail;
scanf("%d%d",&lis[tail].no,&lis[tail].value);
tailc++;
lisc[tailc]=lis[tail].no;
break;
default:
doit=false;
}
} sort(&lis[],&lis[tail+],op);
int o=;
memset(num,,sizeof(num));
for (int i=;i<=tail;i++)
{
if (o!=lis[i].no)
{
o=lis[i].no;
start[o]=i;
}
num[o]++;
} clean_father(n);
sbttail=;
memset(size,,sizeof(size));
for (int i=;i<=n;i++) insert(sbt[i],lis[start[i]+num[i]-].value,);
for (int i=;i<=m;i++)
if (e[i].enable)
if (getfather(e[i].p)!=getfather(e[i].q))
link(e[i].p,e[i].q); int ansq=tailq;
double ans=;
for (int i=tailtot-;i>=;i--)
switch(listot[i])
{
case 'Q':
if (lisq[tailq].k>&&size[sbt[getfather(lisq[tailq].x)]]>=lisq[tailq].k)
ans+=data[select(sbt[getfather(lisq[tailq].x)],size[sbt[getfather(lisq[tailq].x)]]-lisq[tailq].k+)];
tailq--;
break;
case 'D':
if (getfather(e[lisd[taild]].p)!=getfather(e[lisd[taild]].q))
link(e[lisd[taild]].p,e[lisd[taild]].q);
taild--;
break;
case 'C':
num[lisc[tailc]]--;
del(sbt[getfather(lisc[tailc])],lis[start[lisc[tailc]]+num[lisc[tailc]]].value);
insert(sbt[getfather(lisc[tailc])],lis[start[lisc[tailc]]+num[lisc[tailc]]-].value,last);
tailc--;
}
tt++;
printf("Case %d: %.6f\n",tt,ans/ansq);
} return ;
}
HDU 3726 Graph and Queries 平衡树+前向星+并查集+离线操作+逆向思维 数据结构大综合题的更多相关文章
- HDU 3726 Graph and Queries treap树
题目来源:HDU 3726 Graph and Queries 题意:见白书 思路:刚学treap 參考白皮书 #include <cstdio> #include <cstring ...
- HDU 3726 Graph and Queries (离线处理+splay tree)
Graph and Queries Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- HDU 3726 Graph and Queries(平衡二叉树)(2010 Asia Tianjin Regional Contest)
Description You are given an undirected graph with N vertexes and M edges. Every vertex in this grap ...
- P1197 [JSOI2008]星球大战——链式前向星+并查集
https://www.luogu.org/problem/P1197 这道题算是关闭农场的加强版吧,数据有点大,矩阵存不下: 也是记录删点操作,从后往前加边: 先将每个点都算成一个连通块,然后每连一 ...
- [la P5031&hdu P3726] Graph and Queries
[la P5031&hdu P3726] Graph and Queries Time Limit: 10000/5000 MS (Java/Others) Memory Limit: ...
- HDU 3081 Marriage Match II (网络流,最大流,二分,并查集)
HDU 3081 Marriage Match II (网络流,最大流,二分,并查集) Description Presumably, you all have known the question ...
- HDU 4514 湫湫系列故事——设计风景线(并查集+树形DP)
湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) To ...
- HDU 3081:Marriage Match II(二分图匹配+并查集)
http://acm.hdu.edu.cn/showproblem.php?pid=3081 题意:有n个男生n个女生,他们只有没有争吵或者女生a与男生A没有争吵,且女生b与女生a是朋友,因此女生b也 ...
- 【HDOJ】3726 Graph and Queries
Treap的基础题目,Treap是个挺不错的数据结构. /* */ #include <iostream> #include <string> #include <map ...
随机推荐
- 使用 NSUserDefaults 读取和写入自定义对象
众所周知,NSUserDefaults只能保存诸如NSArray.NSDictionary.NSData.NSNumber等基本数据类型,如果我们强制保存自定义的类,就会出现这个错误:Attempt ...
- phabricator 搭建
os:debian7 Installation Guide :https://secure.phabricator.com/book/phabricator/ $ cd /data # 安装目录 da ...
- Python基础篇-day2
主要内容: for循环 while循环 格式化输出(2) 数据统计及记录 ############################################################# 1 ...
- Android PagerAdapter的用法
转http://blog.csdn.net/look85/article/details/8563906 在写这个之前,真心需要吐槽一下…关于Android开发中,PageAdapter的用法在网上能 ...
- ios 给view添加一个渐变的背景色
CAGradientLayer *gradientLayer = [[CAGradientLayer alloc] init]; gradientLayer.colors = @[(__bridge ...
- nginx源码学习资源
http://www.cnblogs.com/yjf512/archive/2012/06/13/2548515.html nginx源码学习是一个痛苦又快乐的过程,下面列出了一些nginx的学习资源 ...
- C# 16位的GUDI
引用: http://www.cnblogs.com/lcwzj/archive/2009/04/16/1436992.html 当我们想要获得一个唯一的key的时候,通常会想到GUID.这个key ...
- vbs 读取txt是读取特定的行
读取第三行的内容:Set fso = CreateObject("scripting.filesystemobject")path="A.TXT文件的路径"Se ...
- Entity Framework技巧系列之二 - Tip 6 - 8
提示6. 如何及何时使用贪婪加载 什么时候你需要使用贪婪加载? 通常在你的程序中你知道对查询到的实体将要进行怎样的操作. 例如,如果你查询一个订单以便为一个客户重新打印,你知道没有组成订单的项目即产品 ...
- python--lambda和def函数
1.Python lambda和Python def区别分析 Python支持一种有趣的语法,它允许你快速定义单行的最小函数.这些叫做lambda的函数,是从Lisp借用来的,可以用在任何需要函数的地 ...