bzoj 1969: [Ahoi2005]LANE 航线规划

好久不写图论题好虚啊,m打成n RE了3次QAQ人生耻辱啊。

首先假设没有删边,显然答案是两点间桥的数量。

于是求边双连通分量,缩点缩边,只留下桥。

所以剩下的肯定是树啦。

于是询问变成了求树上两点间距离,求下LCA就可以直接做了。

但是还有删边操作。

删边肯定不好处理。

反过来改成加边操作。

两点与LCA形成了一个边双联通分量,于是把他们缩起来。

直接缩不可取,考虑缩点的后果是子树被提升了相同的高度,于是考虑把点u向祖先v缩边,step by step,缩掉u-fa(u)这条边的后果是u这颗子树的高度被提升了dep(u)-dep(fa(u)),dfs序+BIT维护子树高度,差分之后变成区间修改+单点查询。

然后由于每个点都至多被缩一次(加个并查集维护一下)所以是nlogn的。

所以总的也就是nlogn了。

made竟然写了180多行

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<stack>
#define mk(x,y) make_pair(x,y)
using namespace std;
const int N=30000+5;
const int M=100000+5;
struct Edge{int to,next;}e[M<<1];
int head[N],cnt;
void ins(int u,int v){
	e[++cnt]=(Edge){v,head[u]};head[u]=cnt;
}
void insert(int u,int v){
	ins(u,v);ins(v,u);
}
int dep[N],son[N],siz[N],st[N],ed[N],fa[N],top[N],sz;
void dfs(int u){
	son[u]=0;siz[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;if(v==fa[u])continue;
		fa[v]=u;dep[v]=dep[u]+1;
		dfs(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs(int u,int tp){
	st[u]=++sz;top[u]=tp;
	if(son[u])dfs(son[u],tp);
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v!=son[u]&&v!=fa[u])
		dfs(v,v);
	}
	ed[u]=sz;
}
int lca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	if(dep[u]>dep[v])swap(u,v);
	return u;
}
struct BIT{
	int d[N];
	#define lb(x) (x&-x)
	void add(int x,int v){
		for(;x<=sz;x+=lb(x))d[x]+=v;
	}
	int sum(int x){
		int ans=0;
		for(;x>0;x-=lb(x))ans+=d[x];
		return ans;
	}
	void add(int l,int r,int x){
		add(l,x);add(r+1,-x);
	}
	int query(int x){
		return sum(st[x]);
	}
}T;
void up(int x,int d){
	T.add(st[x],ed[x],-d);
}
int pa[N];
int find(int x){
	return pa[x]==x?x:pa[x]=find(pa[x]);
}
void merge(int f,int t){
	while(f!=t){
		up(f,T.query(f)-T.query(find(fa[f])));
		pa[f]=t;
		f=find(fa[f]);
	}
}
void add(int u,int v){
	u=find(u);v=find(v);
	if(u==v)return;
	int w=lca(u,v);w=find(w);
	merge(u,w);merge(v,w);
}
int query(int x,int y){
	x=find(x);y=find(y);
	if(x==y)return 0;
	int w=lca(x,y);w=find(w);
	return T.query(x)+T.query(y)-2*T.query(w);
}
int bccno[N],dfn[N],dfs_clock,bcc_cnt;
stack<int>s;
int tarjan(int u,int fa){
	int lowu=dfn[u]=++dfs_clock,child=0;
	s.push(u);
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;if(v==fa)continue;
		if(!dfn[v]){
			int lowv=tarjan(v,u);
			lowu=min(lowu,lowv);
			if(lowv>dfn[u]){
				bcc_cnt++;
				while(true){
					int x=s.top();s.pop();
					bccno[x]=bcc_cnt;
					if(x==v)break;
				}
			}
		}else lowu=min(lowu,dfn[v]);
	}
	if(fa<0&&child<2){
		bcc_cnt++;
		while(true){
			int x=s.top();s.pop();
			bccno[x]=bcc_cnt;
			if(x==u)break;
		}
	}
	return lowu;
}
struct Query{
	int c,x,y;
}q[40005];
vector<int>g[N],del[N];
int ans[40005];
int main(){
	//freopen("a.in","r",stdin);
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		sort(g[i].begin(),g[i].end());
		del[i].resize(g[i].size());
	}
	int k=0;
	while(++k){
		scanf("%d",&q[k].c);
		if(q[k].c==-1)break;
		scanf("%d%d",&q[k].x,&q[k].y);
		if(q[k].c==0){
			int it=lower_bound(g[q[k].x].begin(),g[q[k].x].end(),q[k].y)-g[q[k].x].begin();
			del[q[k].x][it]=1;
			it=lower_bound(g[q[k].y].begin(),g[q[k].y].end(),q[k].x)-g[q[k].y].begin();
			del[q[k].y][it]=1;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<g[i].size();j++)
		if(!del[i][j])
		ins(i,g[i][j]);
		g[i].clear();
	}
	tarjan(1,-1);
	for(int u=1;u<=n;u++)
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(bccno[u]!=bccno[v]){
			g[bccno[u]].push_back(bccno[v]);
			g[bccno[v]].push_back(bccno[u]);
		}
	}
	memset(head,0,sizeof(head));cnt=0;
	for(int i=1;i<=bcc_cnt;i++){
		sort(g[i].begin(),g[i].end());
		m=unique(g[i].begin(),g[i].end())-g[i].begin();
		for(int j=0;j<m;j++)
		ins(i,g[i][j]);
	}
	dfs(1);dfs(1,1);
	for(int i=1;i<=sz;i++)
	T.add(st[i],st[i],dep[i]);
	for(int i=1;i<=sz;i++)pa[i]=i;
	for(int i=k-1;i>=1;i--)
	if(q[i].c)
	ans[i]=query(bccno[q[i].x],bccno[q[i].y]);
	else add(bccno[q[i].x],bccno[q[i].y]);
	for(int i=1;i<k;i++)
	if(q[i].c)
	printf("%d\n",ans[i]);
	return 0;
}

原来可以直接树剖……果然是我太弱了

每次加边操作相当于是把u-lca(u,v)-v之间的所有边权都置为0(初始为1)

每次询问是询问两点间边权和

直接树剖啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

其实才多了20行,而且只慢了100MS

难道是我之前那个版本写挫了QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<stack>
#define mk(x,y) make_pair(x,y)
using namespace std;
const int N=30000+5;
const int M=100000+5;
struct Edge{int to,next;}e[M<<1];
int head[N],cnt;
void ins(int u,int v){
	e[++cnt]=(Edge){v,head[u]};head[u]=cnt;
}
void insert(int u,int v){
	ins(u,v);ins(v,u);
}
int dep[N],son[N],siz[N],pos[N],fa[N],top[N],sz;
void dfs(int u){
	son[u]=0;siz[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;if(v==fa[u])continue;
		fa[v]=u;dep[v]=dep[u]+1;
		dfs(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs(int u,int tp){
	pos[u]=++sz;top[u]=tp;
	if(son[u])dfs(son[u],tp);
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v!=son[u]&&v!=fa[u])
		dfs(v,v);
	}
}
struct Node{
	int l,r,sum;
	bool flag;
}tr[N<<2];
#define lc o<<1
#define rc o<<1|1
void pushup(int o){
	tr[o].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int o){
	if(tr[o].flag){
		tr[lc].flag=tr[rc].flag=true;
		tr[lc].sum=tr[rc].sum=0;
		tr[o].flag=0;
	}
}
void build(int o,int l,int r){
	tr[o].l=l;tr[o].r=r;
	if(l==r)tr[o].sum=1;
	else{
		int mid=l+r>>1;
		build(lc,l,mid);build(rc,mid+1,r);
		pushup(o);
	}
}
void modify(int o,int a,int b){
	int l=tr[o].l,r=tr[o].r;
	if(l==a&&b==r){
		tr[o].flag=1;tr[o].sum=0;
	}else{
		int mid=l+r>>1;
		pushdown(o);
		if(b<=mid)modify(lc,a,b);
		else if(mid<a)modify(rc,a,b);
		else modify(lc,a,mid),modify(rc,mid+1,b);
		pushup(o);
	}
}
int sum(int o,int a,int b){
	int l=tr[o].l,r=tr[o].r;
	if(l==a&&b==r)return tr[o].sum;
	else{
		int mid=l+r>>1;
		pushdown(o);
		if(b<=mid)return sum(lc,a,b);
		else if(mid<a)return sum(rc,a,b);
		else return sum(lc,a,mid)+sum(rc,mid+1,b);
	}
}
void add(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		modify(1,pos[top[u]],pos[u]);
		u=fa[top[u]];
	}
	if(u==v)return;
	if(dep[u]>dep[v])swap(u,v);
	modify(1,pos[son[u]],pos[v]);
}
int query(int u,int v){
	int ans=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		ans+=sum(1,pos[top[u]],pos[u]);
		u=fa[top[u]];
	}
	if(u==v)return ans;
	if(dep[u]>dep[v])swap(u,v);
	ans+=sum(1,pos[son[u]],pos[v]);
	return ans;
}
int bccno[N],dfn[N],dfs_clock,bcc_cnt;
stack<int>s;
int tarjan(int u,int fa){
	int lowu=dfn[u]=++dfs_clock,child=0;
	s.push(u);
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;if(v==fa)continue;
		if(!dfn[v]){
			int lowv=tarjan(v,u);
			lowu=min(lowu,lowv);
			if(lowv>dfn[u]){
				bcc_cnt++;
				while(true){
					int x=s.top();s.pop();
					bccno[x]=bcc_cnt;
					if(x==v)break;
				}
			}
		}else lowu=min(lowu,dfn[v]);
	}
	if(fa<0&&child<2){
		bcc_cnt++;
		while(true){
			int x=s.top();s.pop();
			bccno[x]=bcc_cnt;
			if(x==u)break;
		}
	}
	return lowu;
}
struct Query{
	int c,x,y;
}q[40005];
vector<int>g[N],del[N];
int ans[40005];
int main(){
	//freopen("a.in","r",stdin);
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		sort(g[i].begin(),g[i].end());
		del[i].resize(g[i].size());
	}
	int k=0;
	while(++k){
		scanf("%d",&q[k].c);
		if(q[k].c==-1)break;
		scanf("%d%d",&q[k].x,&q[k].y);
		if(q[k].c==0){
			int it=lower_bound(g[q[k].x].begin(),g[q[k].x].end(),q[k].y)-g[q[k].x].begin();
			del[q[k].x][it]=1;
			it=lower_bound(g[q[k].y].begin(),g[q[k].y].end(),q[k].x)-g[q[k].y].begin();
			del[q[k].y][it]=1;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<g[i].size();j++)
		if(!del[i][j])
		ins(i,g[i][j]);
		g[i].clear();
	}
	tarjan(1,-1);
	for(int u=1;u<=n;u++)
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(bccno[u]!=bccno[v]){
			g[bccno[u]].push_back(bccno[v]);
			g[bccno[v]].push_back(bccno[u]);
		}
	}
	memset(head,0,sizeof(head));cnt=0;
	for(int i=1;i<=bcc_cnt;i++){
		sort(g[i].begin(),g[i].end());
		m=unique(g[i].begin(),g[i].end())-g[i].begin();
		for(int j=0;j<m;j++)
		ins(i,g[i][j]);
	}
	dfs(1);dfs(1,1);
	build(1,1,bcc_cnt);
	for(int i=k-1;i>=1;i--)
	if(q[i].c)
	ans[i]=query(bccno[q[i].x],bccno[q[i].y]);
	else add(bccno[q[i].x],bccno[q[i].y]);
	for(int i=1;i<k;i++)
	if(q[i].c)
	printf("%d\n",ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值