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

来源:互联网 发布:视唱练耳软件 编辑:程序博客网 时间:2024/05/01 08:48

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

#include<iostream>#include<cstdio>#include<map>#define ll long longusing 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<iostream>#include<cstdio>#include<vector>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;i<len;i++)if(!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;i<len;i++)if(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;i<n;i++){        scanf("%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<iostream>#include<cstdio>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(maxn<tmp){            maxn=tmp;son[u]=des[i];        }        siz[u]+=tmp;    }    return ++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<n;i++){        scanf("%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<iostream>#include<cstdio>#include<vector>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;i<len;i++){        if(!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;}
0 0
原创粉丝点击