您的位置:首页 > 其它

NOIP模拟题 2016.10.6 [并查集] [联通性] [Tarjan]

2016-10-06 16:44 519 查看
T1 ,图的连通性

题意:给一个图,支持删除操作,询问任意时刻两个点的连通性

观察到只有删除操作,那么可以考虑倒着处理

先把所有将来会删除的边删掉,然后从最后一组询问倒着处理

并查集维护连通性

这里的强制在线是骗人的

在任意时刻图中剩下的边数都是可以离线处理出来的

一个小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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  图论 Tarjan 并查集 LCT