您的位置:首页 > 其它

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

2016-10-12 18:06 309 查看
考试总结:

解题报告:

一. 图的连通性:

题意:给定一图,动态删边,动态求是否连通,且查询中输入的变量需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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息