NOIP模拟题 by天津南开中学 莫凡[tarjan][树剖][并查集]
2016-10-12 18:06
309 查看
考试总结:
解题报告:
一. 图的连通性:
题意:给定一图,动态删边,动态求是否连通,且查询中输入的变量需xor当前边数才为最终输入数据;
分析:只删边则可以逆向建边用并查集查询是否连通,并查集基本上也是现阶段唯一一种可以在线快速求联通的算法了;
具体实现的话,先把边用康托展开转化为n+1进制的数,再用map去映射一个编号,然后两个mapy一个通过编号存储边出现次数,一个通过编号存储这条边的数便于反向查找;然后删边用map映射编号然后删map就可以了,最后反向连图加求联通;
程序:
二. 树的连通性:
题意:给一棵树,动态查询是否相连,断边以及改变点的权值,点的权值在相连与否是会用,且输入变量必须xor上一次查询的答案才为真正的输入数据;
分析:暴力并查集可以直接怼过去……
标程:树链剖分;
Hzx: LCT O(nlogn)
类似于树链剖分的轻重链剖分,把整棵树用实边和虚边连接,实边部分是一条链,用Splay维护
然后删除就把实边变成虚边,并把虚边删掉。
暴力水过版,记得改父节点。
认真写的树链剖分:
三. 逻辑的连通性:
题意:强连通分量模板题;
分析:同上;
解题报告:
一. 图的连通性:
题意:给定一图,动态删边,动态求是否连通,且查询中输入的变量需xor当前边数才为最终输入数据;
分析:只删边则可以逆向建边用并查集查询是否连通,并查集基本上也是现阶段唯一一种可以在线快速求联通的算法了;
具体实现的话,先把边用康托展开转化为n+1进制的数,再用map去映射一个编号,然后两个mapy一个通过编号存储边出现次数,一个通过编号存储这条边的数便于反向查找;然后删边用map映射编号然后删map就可以了,最后反向连图加求联通;
程序:
#include<iostream> #include<cstdio> #include<map> #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<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; }
相关文章推荐
- NOIP模拟题 2016.10.6 [并查集] [联通性] [Tarjan]
- NOIP模拟题 2016.10.31 [DP] [搜索] [并查集]
- NOIP模拟题 2016.11.18 [数论] [计数] [并查集]
- 【NOIP 模拟题】刺杀大使(二分答案+并查集)
- 湖南NOIP集训模拟题DAY1 BY ExfJOE [贪心][DP][二分]
- noip模拟题 2017.10.28 -kmp -Tarjan -鬼畜的优化
- 【NOIP 模拟题】最小生成树(Tarjan求桥)
- [NOIP模拟题][位运算][建模][并查集]
- 【NOIP模拟题】Graph(tarjan+dfs)
- NOIP模拟题[递推][并查集][DP]
- C++——NOIP模拟题——中位数
- 【NOIP模板】 并查集
- 【NOIP 模拟题】邮递员送信(最短路)
- LCA-并查集+tarjan-poj2874
- NOIP 2008提高组第三题题解by rLq
- [Noip模拟题]砍树
- NOIP 模拟题 简单题 随便做 题解与总结
- NOIP模拟题 2016.10.18 [二分答案] [从上到下的树形DP] [链表翻转]
- NOIP模拟 做运动【并查集+最短路】
- NOIP模拟题 2016.11.4 [数论] [费马小定理] [最短路] [建图]