bzoj 3669: [Noi2014]魔法森林
来源:互联网 发布:五子棋c语言判断胜负 编辑:程序博客网 时间:2024/05/21 06:49
3669: [Noi2014]魔法森林
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 1731 Solved: 1047
[Submit][Status][Discuss]
Description
为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。
只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。
Input
第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。
Output
输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。
Sample Input
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
【输入样例2】
3 1
1 2 1 1
Sample Output
32
【样例说明1】
如果小E走路径1→2→4,需要携带19+15=34个守护精灵;
如果小E走路径1→3→4,需要携带17+17=34个守护精灵;
如果小E走路径1→2→3→4,需要携带19+17=36个守护精灵;
如果小E走路径1→3→2→4,需要携带17+15=32个守护精灵。
综上所述,小E最少需要携带32个守护精灵。
【输出样例2】
-1
【样例说明2】
小E无法从1号节点到达3号节点,故输出-1。
HINT
2<=n<=50,000
0<=m<=100,000
1<=ai ,bi<=50,000
Source
题解:并查集+LCT先说一下这个题中学到的一个不错的思想:化边权为点权,之前写树链剖分的时候曾经把边权下放为点权,然后进行处理,但是这种方式用在这里显然不合适,因为LCT 动态树会旋转换根,所有无法下放,那么怎么做呢?因为不管怎么改变树的形态,两个点确定一条边的关系是不变的,所有我们可以把边缩成一点,然后把边权给这个点的点权,然后从缩成的这个点向这条边的两个端点连边,这样就可以解决边权的问题。
因为这个题要维护两个边权,并且要求最后的答案使路径上两个最大边权的和最小,直接两个一块处理,很难搞。所有我们把一个边权按从小到大的顺序排序,然后每次往树中加边,有点类似最小生成树的方式。但是加边时必须保证不能出现环,如果出现环怎么办呢?我们可以在lct中维护每个路径中最大值所在的位置,如果加入该边出现环的话,就判断一下这条路径上的最大值是否大于当前边的权值,如果大于,就拆边建立新边。LCT中只是维护了一个权值,那么我们要求两个权值的和最小,那么如何更新答案能?其实很简单,判断一下起点和终点是否连通,如果连通就用当前边的未加入LCT的权值和LCT中维护的1到N的最大值来更新答案,因为是升序排列,所有可以保证该边未加入LCT的权值一定是最大的。那么肯定有疑问,会不会1到N的路径上根本就没有新加入的那条边呢,那么这样这条路径上一个权值的最大值不就多算了吗?首先这种情况会发生,但是并不会影响最终的答案。因为如果路径上没有这条边,那么之前1到N就是连通的,那么之前在加入1到N中某条路径的时候一定已经计算过正确的答案了,因为当前边的未加入LCT的权值和LCT中维护的1到N的最大值一定比那个答案要大,所有不会有影响。
总之,思路清晰+不手残,就可以AC了。
说一下自己遇到的一个问题,就是在splay时,如果每次都用memset给st数组清零的话,会TLE,慢到你不能想象
如果不需要拆边而两个点又在一个集合中的话,后面的连边操作就跳过。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 300003 using namespace std; int n,m; int fa[N],ch[N][2],maxn[N],key[N],rev[N],size[N]; int mp[N],top,st[N]; struct data { int a,b,x,y; };data tr[N]; int cmp(data a,data b) { return a.a<b.a; } void pushdown(int x) { if (!rev[x]) return; rev[ch[x][0]]^=1; rev[ch[x][1]]^=1; swap(ch[x][0],ch[x][1]); rev[x]=0; } int isroot(int x) { return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x; } int get(int x) { return ch[fa[x]][1]==x; } void update(int x) { int l=ch[x][0]; int r=ch[x][1]; size[x]=x; if (key[size[l]]>key[size[x]]) size[x]=size[l]; if (key[size[r]]>key[size[x]]) size[x]=size[r]; } void rotate(int x) { int y=fa[x]; int z=fa[y]; int which=get(x); if (!isroot(y)) ch[z][ch[z][1]==y]=x; fa[x]=z; ch[y][which]=ch[x][which^1]; fa[ch[y][which]]=y; ch[x][which^1]=y; fa[y]=x; update(y); update(x); } void splay(int x) { top=0; st[++top]=x; for (int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i]; for (int i=top;i>0;i--) pushdown(st[i]); while (!isroot(x)) { int y=fa[x]; if (!isroot(y)) rotate(get(x)==get(y)?y:x); rotate(x); } } void access(int x) { int t=0; while (x) { splay(x); ch[x][1]=t; update(x); t=x; x=fa[x]; } } void rever(int x) { access(x); splay(x); rev[x]^=1; } void link(int x,int y) { rever(x); fa[x]=y; splay(x); } void cut(int x,int y) { rever(x); access(y); splay(y); fa[x]=ch[y][0]=0; update(y); } int find(int x) { if (mp[x]==x) return x; mp[x]=find(mp[x]); return mp[x]; } int seek(int x,int y) { rever(x); access(y); splay(y); return size[y]; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d%d%d",&tr[i].x,&tr[i].y,&tr[i].a,&tr[i].b); sort(tr+1,tr+m+1,cmp); for (int i=1;i<=n;i++) mp[i]=i; int ans=1000000000; for (int i=1;i<=m;i++) { int k=find(tr[i].x); int t=find(tr[i].y); if (k!=t) { mp[t]=k; } else { int l=seek(tr[i].x,tr[i].y); if (key[l]>tr[i].b) { cut(l,tr[l-n].x); cut(l,tr[l-n].y); } else { if (find(1)==find(n)) ans=min(ans,key[seek(1,n)]+tr[i].a); continue; } } key[i+n]=tr[i].b; size[i+n]=i+n; link(tr[i].x,n+i); link(tr[i].y,n+i); if (find(1)==find(n)) { int l=seek(1,n); ans=min(key[l]+tr[i].a,ans); } } if (ans==1000000000) printf("-1\n"); else printf("%d\n",ans); return 0; }
- bzoj 3669: [Noi2014]魔法森林
- bzoj 3669: [Noi2014]魔法森林
- bzoj 3669: [Noi2014]魔法森林
- BZOJ 3669: [Noi2014]魔法森林
- BZOJ 3669: [Noi2014]魔法森林
- bzoj 3669: [Noi2014]魔法森林
- BZOJ 3669 NOI2014 魔法森林 SPFA
- BZOJ 3669 [Noi2014]魔法森林 LCT
- 【LCT】BZOJ 3669: [Noi2014]魔法森林
- bzoj 3669 NOI2014 魔法森林 [LCT]
- [BZOJ 3669][Noi2014]魔法森林:SPFA
- BZOJ 3669 [Noi2014]魔法森林 LCT
- BZOJ 3669: [Noi2014]魔法森林 LCT
- BZOJ 3669 [Noi2014]魔法森林 Kruskal+LCT
- [BZOJ]3669: [Noi2014]魔法森林 lct
- BZOJ 3669 . JZOJ 3754. 【NOI2014】魔法森林
- 3669: [Noi2014]魔法森林
- 3669: [Noi2014]魔法森林
- Pinot中的Inverted Index源码分析
- 乌云章华鹏:如何构建高效的安全运维服务平台
- android toast使用总结
- poj 3273 Monthly Expense
- 后续_Learn Java for Android Development (第三版)
- bzoj 3669: [Noi2014]魔法森林
- mysql数据库设计原则
- 第一章 JAVA入门_Learn Java for Android Development(第三版)
- CodeForces 665A Buses Between Cities
- linux c++ 多线程编程
- c++作业4
- 安装ntfs-3g挂载ntfs格式硬盘=centos7自学之三
- CodeForces 665B Shopping
- PHP名言警句