1. 程式人生 > >NOIP模擬題 2016.10.6 [並查集] [聯通性] [Tarjan]

NOIP模擬題 2016.10.6 [並查集] [聯通性] [Tarjan]

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;
    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;
}