[APIO2019]桥梁

题目链接
(luogu上现在这个题时限非常紧,题解区的代码也不一定能过)

题意:

有一张n个点,m条边的无向图,然后每条边有权值;有两种操作:

1 将一条边的权值修改.
2 询问从某个点开始,只允许进过大于给定权值的边,最多可以到达几个点.

解法

首先考虑如果没有修改操作,应该怎么做:
显然可以把询问离线下来,并按给定的权值从大到小排序,然后用并查集维护答案.

如果有操作呢?我们也延续上面的思路,用并查集维护答案,然后就是每 l o g ( n ) m \sqrt {log(n)*m} 次操作分为一块,对于块内的查询操作,显然未在块内被修改过的边就不会被修改,所以可以直接排序好,用没有修改操作的方法维护,然后对于一次查询,暴力遍历所有修改操作,考虑修改操作的时间顺序,判断是否需要修改.
然后一个块的操作处理完以后,把所有修改操作都处理了,此时边的权值发生了改变,需要重新排序.

注意

这个题写了我一下午,主要是在块内的修改操作上一直有细节没有解决:一条边可能被修改了多次,只有最近的一次才是有用的.然后如果某条边在查询前就被修改过,查询后又被修改,还是只能用查询前的(可能不太好理解,可以看代码).
然后排序不能每次直接暴力排m条边,而是只排被修改的边,然后合并回去的时候需要归并.所以一开始边要是有序的(一开始需要排一次序).

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,Q;
struct node{
	int u,v,w,id;
	node(int a=0,int b=0,int c=0,int d=0){u=a,v=b,w=c,id=d;}
}e[maxn],e1[maxn],e2[maxn];
struct ak{
	int op,s,w,id;
	ak(int a=0,int b=0,int c=0,int d=0){op=a,s=b,w=c;id=d;}
}q[maxn],alfa[maxn],beta[maxn];
int cnt;
bool cmp(node a,node b){
	return a.w>b.w;
}
bool cmp2(ak a,ak b){
	return a.w>b.w;
}
int op[maxn],f[maxn];
inline int find(int x){
	return f[x]==x?f[x]:find(f[x]);
}
int cnt1,cnt2,top,sz[maxn],dep[maxn];
node st[maxn];
inline void merge(int u,int v){
	int x=find(u),y=find(v);
	if(dep[x]>dep[y])swap(x,y);
	if(x==y)return ;
	f[x]=y;sz[y]+=sz[x];if(dep[x]==dep[y])dep[y]++;
}
inline void merge2(int u,int v){
	int x=find(u),y=find(v);
	//printf("%d %d\n",x,y);
	if(sz[x]>sz[y])swap(x,y);
	if(x==y)return ;
	st[++top]=node(x,f[x],y,sz[x]);
	f[x]=y;sz[y]+=sz[x];if(dep[x]==dep[y])dep[y]++;
}
int g[maxn],ans[maxn],pos[maxn],vis[maxn];
void work(){
	cnt1=cnt2=0;
	for(int i=1;i<=n;i++){
		f[i]=i;sz[i]=1;dep[i]=1;
	}
	for(int i=1;i<=m;i++){
		g[i]=0;
	}
	for(int i=1;i<=cnt;i++){
		if(q[i].op==1){alfa[++cnt1]=q[i];g[q[i].s]=1;}
		else beta[++cnt2]=q[i];
	}
	for(int i=1;i<=m;i++)pos[e[i].id]=i;
	sort(beta+1,beta+1+cnt2,cmp2);
	int p=1;
	for(int i=1;i<=cnt2;i++){
		while(e[p].w>=beta[i].w&&p<=m){
			if(g[e[p].id]){
				p++;
			}
			else{
				//printf("%d %d %d\n",e[p].u,e[p].v,e[p].w);
				merge(e[p].u,e[p].v);p++;
			}
		}
		int flag=1;
		for(int j=1;j<=cnt1;j++){
			if(alfa[j].id>beta[i].id){
				for(int k=j-1;k>=1;k--){
					if(vis[pos[alfa[k].s]])continue;
					vis[pos[alfa[k].s]]=1;
					if(alfa[k].w>=beta[i].w){
						//printf("%d %d %d?\n",e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v,alfa[k].w);
						merge2(e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v);
					}
				}
				for(int k=j;k<=cnt1;k++){
					if(vis[pos[alfa[k].s]])continue;
					if(e[pos[alfa[k].s]].w>=beta[i].w){
						//printf("%d %d %d.\n",e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v,e[pos[alfa[k].s]].w);
						merge2(e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v);
					}
				}
				for(int k=1;k<=cnt1;k++)vis[pos[alfa[k].s]]=0;
				flag=0;
				break;
			}
		}
		if(flag){
			for(int k=cnt1;k>=1;k--){
				if(vis[pos[alfa[k].s]])continue;
				vis[pos[alfa[k].s]]=1;
				if(alfa[k].w>=beta[i].w){
					merge2(e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v);
				}
			}
			for(int k=1;k<=cnt1;k++)vis[pos[alfa[k].s]]=0;
		}
		ans[beta[i].id]=sz[find(beta[i].s)];
		//printf("%d %d!\n",beta[i].s,ans[beta[i].id]);
		while(top){
			int u=st[top].u,val=st[top].v,v=st[top].w;int flag=st[top].id;
			f[u]=val;sz[v]-=flag;sz[u]=flag;
			top--;
		}
	}
	for(int i=1;i<=cnt1;i++){
		e[pos[alfa[i].s]].w=alfa[i].w;
	}
	int tot1=0,tot2=0,tot3=0;
	for(int i=1;i<=m;i++){
		if(g[e[i].id])e2[++tot2]=e[i];
		else e1[++tot1]=e[i];
	}
	sort(e2+1,e2+1+tot2,cmp);
	int p1=1,p2=1;
	while(p1<=tot1&&p2<=tot2){
		if(e1[p1].w>e2[p2].w){tot3++;e[tot3]=e1[p1];p1++;}
		else {tot3++;e[tot3]=e2[p2];p2++;}
	}
	while(p1<=tot1){tot3++;e[tot3]=e1[p1],p1++;}
	while(p2<=tot2){tot3++;e[tot3]=e2[p2],p2++;}
}
int main(){
	//freopen("luogu5443.in","r",stdin);
	//freopen("luogu5443.out","w",stdout);
	n=read(),m=read();
	int lim=sqrt(m*log(n));
	//printf("%d\n",lim);
	for(int i=1;i<=m;i++){
		e[i].u=read(),e[i].v=read();e[i].w=read();e[i].id=i;
	}
	sort(e+1,e+1+m,cmp);
	Q=read();
	for(int i=1;i<=Q;i++){
		q[++cnt].op=read();q[cnt].s=read(),q[cnt].w=read();q[cnt].id=i;
		op[i]=q[cnt].op;
		if(cnt==lim){work();cnt=0;}
	}
	if(cnt)work();
	for(int i=1;i<=Q;i++){
		if(op[i]==2){
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}

发布了62 篇原创文章 · 获赞 1 · 访问量 1000

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103809717
今日推荐