NOIP模拟题 2016.10.6 [并查集] [联通性] [Tarjan]
2016-10-06 16:44
519 查看
T1 ,图的连通性
题意:给一个图,支持删除操作,询问任意时刻两个点的连通性
观察到只有删除操作,那么可以考虑倒着处理
先把所有将来会删除的边删掉,然后从最后一组询问倒着处理
并查集维护连通性
这里的强制在线是骗人的
在任意时刻图中剩下的边数都是可以离线处理出来的
一个小trick
重边,删除的时候只删除一条
用set 或者map判断当前该边的数量 {cnt,disable}
当cnt==disable时,两点不连通
T2 , 树的连通性:
题意: 同T1,只不过没有重边和环。
据说可以暴力水过
O(n^2)暴力:用父亲节点表示法,删掉一条边的时候,直接把更深的那个节点father[v]=v
每次都直接暴力地找根节点
O(nlogn) :丁神的解法
维护一个dfs序列,以及每个节点的深度
然后把未删边的树用LCA处理
删边的时候把更深的那个节点做上标记
求出LCA之后递归地判断从u到LCA的路径上是不是所有的点都是联通的,这个操作O(logn)的
均摊时间复杂度O(nlogn)
RE的标程,O(nlogn),用的是树链剖分
我用的LCT O(nlogn)
类似于树链剖分的轻重链剖分,把整棵树用实边和虚边连接,实边部分是一条链,用Splay维护
然后删除就把实边变成虚边,并把虚边删掉。
LCT:
T3:
题意 :有向图Tarjan
题意:给一个图,支持删除操作,询问任意时刻两个点的连通性
观察到只有删除操作,那么可以考虑倒着处理
先把所有将来会删除的边删掉,然后从最后一组询问倒着处理
并查集维护连通性
这里的强制在线是骗人的
在任意时刻图中剩下的边数都是可以离线处理出来的
一个小trick
重边,删除的时候只删除一条
用set 或者map判断当前该边的数量 {cnt,disable}
当cnt==disable时,两点不连通
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) const int INF=0x3f3f3f3f; const int maxn = 100005; const int maxm = 150005; const int maxq = 100005; inline void read(int &x) { x = 0; int flag = 1; char ch = getchar(); while(ch<'0' || ch>'9') { ch=getchar(); if(ch == '-') flag = -1; } while(ch>='0' && ch<='9') { x*=10; x+=ch-'0'; ch = getchar(); } x *= flag; } struct Road { int x,y; int cnt,disable; bool operator < (const Road t) const { if(x ^ t.x) return x < t.x; else return y < t.y; } }road[maxm]; set <Road> st; set <Road> ::iterator it; struct Query { int x,y; int id; int ans; }que[maxq]; int n,m,q; int maxedge; void init() { maxedge=0; read(n); read(m); read(q); for(int i=1;i<=m;i++) { int x,y; read(x); read(y); if(x>y) swap(x,y); it = st.find((Road){x,y,0,0}); if(it!=st.end()) { Road tmp = (*it); st.erase(it); tmp.cnt++; st.insert(tmp); } else st.insert((Road){x,y,1,0}); } for(it = st.begin(); it!=st.end(); it++) road[++maxedge] = (*it); int cnt = m; for(int i=1;i<=q;i++) { int x,y; read(que[i].id); read(x); read(y); x ^= cnt; y ^= cnt; if(x>y) swap(x,y); que[i].x = x; que[i].y = y; if(que[i].id==1) { cnt--; Road t = (Road) { x,y,0,0 }; int pos = lower_bound(road+1,road+maxedge+1,t)-road; road[pos].disable++; } } } int fa[maxn]; int find(int x) { return x^fa[x]?fa[x]=find(fa[x]):x; } void join(int x,int y) { int t1 = find(x) , t2 = find(y); if(t1==t2) return; fa[t2]=t1; } bool query(int x,int y) { int t1 = find(x) , t2 = find(y); return t1==t2; } void work() { for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=maxedge;i++) if(road[i].disable<road[i].cnt) join(road[i].x,road[i].y); for(int i=q;i>=1;i--) if(que[i].id==1) join(que[i].x,que[i].y); else que[i].ans = query(que[i].x,que[i].y); for(int i=1;i<=q;i++) if(que[i].id==2) printf("%d\n",que[i].ans); } int main() { freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); init(); work(); return 0; }
T2 , 树的连通性:
题意: 同T1,只不过没有重边和环。
据说可以暴力水过
O(n^2)暴力:用父亲节点表示法,删掉一条边的时候,直接把更深的那个节点father[v]=v
每次都直接暴力地找根节点
O(nlogn) :丁神的解法
维护一个dfs序列,以及每个节点的深度
然后把未删边的树用LCA处理
删边的时候把更深的那个节点做上标记
求出LCA之后递归地判断从u到LCA的路径上是不是所有的点都是联通的,这个操作O(logn)的
均摊时间复杂度O(nlogn)
RE的标程,O(nlogn),用的是树链剖分
我用的LCT O(nlogn)
类似于树链剖分的轻重链剖分,把整棵树用实边和虚边连接,实边部分是一条链,用Splay维护
然后删除就把实边变成虚边,并把虚边删掉。
LCT:
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) const int INF=0x3f3f3f3f; const int maxn = 200005; inline void read(int &x) { x = 0; int flag = 1; char ch = getchar(); while(ch<'0' || ch>'9') { ch=getchar(); if(ch == '-') flag = -1; } while(ch>='0' && ch<='9') { x*=10; x+=ch-'0'; ch = getchar(); } x *= flag; } struct Node { int ch[2]; int fa; int rev; bool isroot; int val; Node() { ch[0]=ch[1]=fa=rev=0;isroot=true; } }node[maxn]; int maxnode; #define fa(x) node[x].fa #define ch(x,d) node[x].ch[d] #define rev(x) node[x].rev #define isroot(x) node[x].isroot #define val(x) node[x].val inline void pushdown(int x) { if(!x) return; if(!rev(x)) return; rev(x)^=1; swap(ch(x,0),ch(x,1)); if(ch(x,0)) rev(ch(x,0))^=1; if(ch(x,1)) rev(ch(x,1))^=1; } int sta[maxn],top; inline void update(int x) { while(!isroot(x)) { sta[++top]=x; x=fa(x); } sta[++top]=x; while(top) pushdown(sta[top--]); } inline void rotate(int x) { int y=fa(x),z=fa(y); int l=(ch(y,1)==x),r=l^1; if(isroot(y)) isroot(y)=false,isroot(x)=true; else ch(z,ch(z,1)==y)=x; fa(ch(x,r))=y; fa(y)=x; fa(x)=z; ch(y,l)=ch(x,r); ch(x,r)=y; } inline void splay(int x) { update(x); while(!isroot(x)) { int y=fa(x),z=fa(y); if(!isroot(y)) if(ch(y,0)==x ^ ch(z,0)==y) rotate(x); else rotate(y); rotate(x); } } inline int access(int x) { int y = 0; do { splay(x); isroot(ch(x,1))=true; isroot(ch(x,1)=y)=false; x=fa(y=x); }while(x); return y; } void make_root(int x) { int rt = access(x); rev(rt)^=1; } void cut(int u,int v) { make_root(u); access(v); splay(v); fa(ch(v,0))=0; isroot(ch(v,0))=true; ch(v,0)=0; } void join(int u,int v) { access(u); splay(u); rev(u)^=1; fa(u)=v; } int find_root(int x) { access(x); splay(x); while(ch(x,0)) x=ch(x,0); return x; } bool query(int u,int v) { int t1 = find_root(u); int t2 = find_root(v); return t1==t2; } int n,m; int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); read(n); read(m); for(int i=1;i<=n;i++) read(node[i].val); for(int i=1;i<n;i++) { int x,y; read(x); read(y); join(x,y); } int lastans = 0; for(int i=1;i<=m;i++) { int op,x,y; read(op); read(x); read(y); x ^= lastans; y ^= lastans; switch(op) { case 1: cut(x,y); break; case 2: lastans = query(x,y) ? val(x)*val(y) : val(x)+val(y); printf("%d\n",lastans); break; case 3: val(x)=y; } } return 0; }
T3:
题意 :有向图Tarjan
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) typedef long long LL; inline void read(int &x) { x = 0; int flag = 1; char ch = getchar(); while(ch<'0' || ch>'9') { ch=getchar(); if(ch == '-') flag = -1; } while(ch>='0' && ch<='9') { x*=10; x+=ch-'0'; ch = getchar(); } x *= flag; } const int INF=0x3f3f3f3f; const int maxn = 50005; const int maxm = 600005; struct Edge { int to,next; }edge[maxm]; int head[maxn]; int maxedge; inline void addedge(int u,int v) { edge[++maxedge] = (Edge) { v,head[u] }; head[u] = maxedge; } int n,m; void init() { read(n); read(m); memset(head,-1,sizeof(head)); maxedge=-1; for(int i=1;i<=m;i++) { int x,y; read(x); read(y); addedge(x,y); } } int dfn[maxn],dfs_clock,scc_cnt; int sccno[maxn],size[maxn]; bool insta[maxn]; stack <int> sta; int dfs(int u) { int lowu = dfn[u] = ++dfs_clock; 12adf sta.push(u); insta[u]=true; for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; if(!dfn[v]) { int lowv = dfs(v); smin(lowu,lowv); } else if(insta[v]) smin(lowu,dfn[v]); } if(lowu>=dfn[u]) { scc_cnt++; int tmp; do { tmp = sta.top(); sta.pop(); insta[tmp]=false; sccno[tmp] = scc_cnt; size[scc_cnt]++; }while(tmp^u); } return lowu; } void Tarjan() { memset(dfn,0,sizeof(dfn)); memset(sccno,0,sizeof(sccno)); memset(size,0,sizeof(size)); dfs_clock=scc_cnt=0; for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); } LL work() { Tarjan(); LL ans = 0; for(int i=1;i<=scc_cnt;i++) if(size[i]>=2) ans += (LL) (size[i]-1)*size[i]/2; return ans; } int main() { freopen("logic.in","r",stdin); freopen("logic.out","w",stdout); init(); printf(AUTO,work()); return 0; }
相关文章推荐
- NOIP模拟题 by天津南开中学 莫凡[tarjan][树剖][并查集]
- NOIP模拟题 2016.11.18 [数论] [计数] [并查集]
- NOIP模拟题 2016.10.31 [DP] [搜索] [并查集]
- 【NOIP 模拟题】刺杀大使(二分答案+并查集)
- [NOIP模拟题][位运算][建模][并查集]
- NOIP模拟题[递推][并查集][DP]
- noip模拟题 2017.10.28 -kmp -Tarjan -鬼畜的优化
- 【NOIP模拟题】Graph(tarjan+dfs)
- 【NOIP 模拟题】最小生成树(Tarjan求桥)
- noip关押罪犯 并查集做法
- [NOIP模拟题][费马小定理][搜索][建图][SPFA]
- [Noip模拟题]绿豆蛙的归宿
- NOIP 模拟题 奇怪的字符串
- wikioi 1069 关押罪犯 2010年NOIP全国联赛提高组(并查集)
- 【NOIP 模拟题】[T1]质数生成器(线性筛+乱搞)
- NOIP 模拟题 【hao】【kun】【nan】
- 正确答案 全国信息学奥林匹克联赛( ( NOIP2014) 复 赛 模拟题 Day1 长乐一中
- noip模拟题11.15 距noip2016还剩三天
- 【NOIP模拟题】【数学归纳法】【容斥原理】2016.11.18 第一题 信 题解
- 【NOIP2013】【ygylca】 货车运输带权并查集