NOIP模拟题 by天津南开中学 莫凡[tarjan][树剖][并查集]

考试总结:
解题报告:
一. 图的连通性:
题意:给定一图,动态删边,动态求是否连通,且查询中输入的变量需xor当前边数才为最终输入数据;
分析:只删边则可以逆向建边用并查集查询是否连通,并查集基本上也是现阶段唯一一种可以在线快速求联通的算法了;
具体实现的话,先把边用康托展开转化为n+1进制的数,再用map去映射一个编号,然后两个mapy一个通过编号存储边出现次数,一个通过编号存储这条边的数便于反向查找;然后删边用map映射编号然后删map就可以了,最后反向连图加求联通;
程序:

#include
#include
#include
#define ll long long
using namespace std;
map<long long ,int>roa;
const int maxn=150000+5;
int mapy[maxn],n,m,t,sta,fin,fa[maxn],inde,tmp1[maxn];
int tmp2[maxn],tmp3[maxn],ans[maxn];
long long mapy2[maxn];
ll calc(ll  x,ll y,ll n)
{
    return x*(n+1)+y;
}
int lookfor(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=lookfor(fa[x]);
}
void unionion(int x,int y)
{
    x=lookfor(x);y=lookfor(y);
    fa[x]=y;
}
int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    scanf("%d %d %d",&n,&m,&t);
    for(int i=1;i<=m;i++){
        scanf("%d %d",&sta,&fin);
        if(sta>fin)swap(sta,fin);
        if(!roa[calc(sta,fin,n)]){
        roa[calc(sta,fin,n)]=++inde;
        mapy2[inde]=calc(sta,fin,n);
        }
        mapy[roa[calc(sta,fin,n)]]++;
    }
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=t;i++){
    scanf("%d %d %d",&tmp1[i],&tmp2[i],&tmp3[i]);
    tmp2[i]^=m;tmp3[i]^=m;if(tmp2[i]>tmp3[i])swap(tmp2[i],tmp3[i]);
    if(tmp1[i]==1){
        m--;
        mapy[roa[calc(tmp2[i],tmp3[i],n)]]--;
    }
    }
    for(int i=1;i<=inde;i++)if(mapy[i]){
        unionion(mapy2[i]/(ll)(n+1),mapy2[i]%(ll)(n+1));
    }
    for(int i=t;i>=1;i--){
    if(tmp1[i]==1){
        unionion(tmp2[i],tmp3[i]);
    }
    else {
    if(lookfor(tmp2[i])==lookfor(tmp3[i]))ans[i]=1;
    else ans[i]=0;
    }
    }
    for(int i=1;i<=t;i++)if(tmp1[i]==2)printf("%d\n",ans[i]);
    return 0;
}

二. 树的连通性:
题意:给一棵树,动态查询是否相连,断边以及改变点的权值,点的权值在相连与否是会用,且输入变量必须xor上一次查询的答案才为真正的输入数据;
分析:暴力并查集可以直接怼过去……
标程:树链剖分;
Hzx: LCT O(nlogn)
类似于树链剖分的轻重链剖分,把整棵树用实边和虚边连接,实边部分是一条链,用Splay维护
然后删除就把实边变成虚边,并把虚边删掉。

暴力水过版,记得改父节点。

#include
#include
#include
using namespace std;
const int maxn=2e5+5;
int n,m,sta,fin,used[maxn],tmp1,tmp2,tmp3,lasten;
int fa[maxn],dada[maxn],belonging[maxn];
vector<int>e[maxn],sonn[maxn];
void buildy(int u)
{
    belonging[u]=1;used[u]=1;
    int len=e[u].size();
    for(int i=0;iif(!used[e[u][i]]){
    fa[e[u][i]]=u;sonn[u].push_back(e[u][i]);
    buildy(e[u][i]);}
}
void buildy2(int u)
{
    int len=sonn[u].size();
    for(int i=0;iif(fa[sonn[u][i]]==u){
        belonging[sonn[u][i]]=belonging[u];
        buildy2(sonn[u][i]);
    }
}
void cuty(int x,int y)
{
    if(fa[x]==y){
    belonging[x]=fa[x]=x;buildy2(x);}
    else if(fa[y]==x){
    belonging[y]=fa[y]=y;buildy2(y);}
}
void ser(int x,int y)
{
    if(belonging[x]==belonging[y])
    printf("%d\n",lasten=dada[x]*dada[y]);
    else printf("%d\n",lasten=dada[x]+dada[y]);
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&dada[i]);
    for(int i=1;iscanf("%d %d",&sta,&fin);
        e[sta].push_back(fin);
        e[fin].push_back(sta);
    }
    buildy(1);belonging[1]=1;
    for(int i=1;i<=m;i++){
        scanf("%d %d %d",&tmp1,&tmp2,&tmp3);
        tmp2^=lasten;tmp3^=lasten;
        if(tmp1==1)cuty(tmp2,tmp3);
        else if(tmp1==2)ser(tmp2,tmp3);
        else dada[tmp2]=tmp3;
    }
    return 0;
}

认真写的树链剖分:

#include
#include
using namespace std;
const int maxn=2e5+5;
int dep[maxn],fa[maxn],siz[maxn],son[maxn],top[maxn],w[maxn],fr[maxn],tov[maxn],des[maxn],val[maxn];
int n,m,cnt,sta,fin,tmp1,tmp2,tmp3,lasten;
struct node
{
    int lef,rig,dis;
}a[maxn*4];
void addedge(int sta,int fin)
{
    tov[++cnt]=fr[sta];fr[sta]=cnt;des[cnt]=fin;
}
int dfs1(int u,int ste,int f)
{
    dep[u]=ste;fa[u]=f;
    int maxn=0;
    for(int i=fr[u];i>0;i=tov[i]){
        int tmp=dfs1(des[i],ste+1,u);
        if(maxnreturn ++siz[u];
}
void dfs2(int u,int f)
{
    w[u]=++cnt;top[u]=f;
    if(son[u])dfs2(son[u],f);
    for(int i=fr[u];i>0;i=tov[i])
        if(des[i]!=son[u])dfs2(des[i],des[i]);
}
void build(int u,int lef,int rig)
{
    int mid=(lef+rig)/2;
    a[u].lef=lef;a[u].rig=rig;
    if(lef!=rig)
    build(2*u,lef,mid),build(2*u+1,mid+1,rig);
}
void del(int u,int lef,int rig)
{
    a[u].dis=1;
    int mid=(a[u].lef+a[u].rig)/2;
    if(a[u].lef==lef&&a[u].rig==rig)return;
    else if(lef>mid)del(2*u+1,lef,rig);
    else if(rig<=mid)del(2*u,lef,rig);
    else del(2*u,lef,mid),del(2*u+1,mid+1,rig);
}
int query(int u,int lef,int rig)
{
    int mid=(a[u].lef+a[u].rig)/2;
    if(a[u].lef==lef&&a[u].rig==rig)return a[u].dis;
    else if(lef>mid)return query(2*u+1,lef,rig);
    else if(rig<=mid)return query(2*u,lef,rig);
    else return query(2*u,lef,mid)|query(2*u+1,mid+1,rig);
}
int queryctrl(int va,int vb)
{
     int f1 = top[va], f2 = top[vb];
     while (f1 != f2)
     {
           if (dep[f1] < dep[f2])
           { swap(f1, f2); swap(va, vb); }
           if(query(1 ,w[f1], w[va]))return 1;
           va = fa[f1]; f1 = top[va];
     }
     if (va == vb) return 0;
     if (dep[va] > dep[vb]) swap(va, vb);
     return query(1, w[son[va]], w[vb]); 
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&val[i]);
    for(int i=1;i"%d %d",&sta,&fin);
        if(sta>fin)swap(sta,fin);
        addedge(sta,fin);
    }cnt=0;
    dfs1(1,0,1);dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        scanf("%d %d %d",&tmp1,&tmp2,&tmp3);
        tmp2^=lasten;tmp3^=lasten;
        if(tmp1==1){
        if(fa[tmp2]==tmp3)del(1,w[tmp2],w[tmp2]);
        else del(1,w[tmp3],w[tmp3]);
        }
        else if(tmp1==2){
        if(queryctrl(tmp2,tmp3))printf("%d\n",lasten=val[tmp2]+val[tmp3]);
        else printf("%d\n",lasten=val[tmp2]*val[tmp3]);
        }
        else val[tmp2]=tmp3;
    }
    return 0;
}

三. 逻辑的连通性:
题意:强连通分量模板题;
分析:同上;

#include
#include
#include
using namespace std;
const int maxn=50005;
int n,m,sta,fin,used[maxn],inde,inde2,low[maxn];
int dfn[maxn],stack[maxn],instack[maxn],head;
long long ans,roa[maxn];
vector<int>e[maxn];
void tarjan(int u)
{
    used[u]=1;low[u]=dfn[u]=++inde;
    stack[++head]=u;instack[u]=1;
    int len=e[u].size();
    for(int i=0;iif(!used[e[u][i]]){
        tarjan(e[u][i]);
        low[u]=min(low[u],low[e[u][i]]);
        }
        else if(instack[e[u][i]])low[u]=min(low[u],low[e[u][i]]);
    }
    if(dfn[u]==low[u]){
        ++inde2;
        while(stack[head]!=u){
            instack[stack[head]]=0;roa[inde2]++;head--;
        }
        instack[u]=0;roa[inde2]++;head--;
    }
}
int main()
{
    freopen("logic.in","r",stdin);
    freopen("logic.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d %d",&sta,&fin);
        e[sta].push_back(fin);
    }
    for(int i=1;i<=n;i++)if(!used[i])
    tarjan(i);
    for(int i=1;i<=inde2;i++)ans+=roa[i]*(roa[i]-1LL)/2LL;
    printf("%I64d",ans);
    return 0;
}

你可能感兴趣的:(tarjan,树剖,并查集)