• 2020牛客NOIP赛前集训营提高组(第三场)C牛半仙的妹子Tree【虚树,最短路】


    正题

    题目链接:https://ac.nowcoder.com/acm/contest/7609/C


    题目大意

    给出\(n\)个点的一棵树,\(m\)个时刻各有一个操作

    1. 标记一个点,每个点被标记后的每一个时刻会标记掉周围的点。
    2. 删去所有点的标记
    3. 询问一个点是否有标记

    解题思路

    考虑没有二操作怎么搞,可以理解为标记代表起点,然后跑一遍最短路求出每个点被标记的最短时间。

    如果有二操作的话是不是就很麻烦了,因为它像一个分割符一样切开两段操作。

    那么就直接分开操作就好了!对于每段操作的所有点建立虚树,然后跑最短路就好了。

    时间复杂度\(O(n\log n)\),因为是树就用我们的老朋友\(\text{SPFA}\)就好了

    正解是定期重构+\(\text{SPFA}\)的,懒得写


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define mp(x,y) make_pair(x,y)
    using namespace std;
    const int N=2e5+10,T=18;
    struct node{
    	int to,next,w;
    }a[N<<1];
    int n,m,cnt,qnt,pnt,tot,top,ls[N];
    int dep[N],dfn[N],f[N][T+1],s[N];
    int ve[N],dis[N],t[N],p[N];
    pair<int,int> q[N];bool v[N];
    void addl(int x,int y){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;return;
    }
    void adde(int x,int y){
    	a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=dep[y]-dep[x];
    	a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=dep[y]-dep[x];
    	ve[++pnt]=x;ve[++pnt]=y;return;
    }
    void dfs(int x,int fa){
    	dep[x]=dep[fa]+1;dfn[x]=++cnt;
    	for(int i=ls[x];i;i=a[i].next){
    		int y=a[i].to;
    		if(y==fa)continue;
    		f[y][0]=x;dfs(y,x);
    	}
    	return;
    }
    int LCA(int x,int y){
    	if(dep[x]>dep[y])swap(x,y);
    	for(int i=T;i>=0;i--)
    		if(dep[f[y][i]]>=dep[x])y=f[y][i];
    	if(x==y)return x;
    	for(int i=T;i>=0;i--)
    		if(f[x][i]!=f[y][i])
    			x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    bool cmp(int x,int y)
    {return dfn[x]<dfn[y];}
    void Ins(int x){
    	if(!top){s[++top]=x;return;}
    	int lca=LCA(x,s[top]);
    	while(top>1&&dep[s[top-1]]>dep[lca])
    		adde(s[top-1],s[top]),top--;
    	if(dep[s[top]]>dep[lca])adde(lca,s[top]),top--;
    	if(s[top]!=lca||!top)s[++top]=lca;
    	s[++top]=x;return;
    }
    void solve(){
    	queue<int> q;
    	sort(ve+1,ve+1+pnt);
    	pnt=unique(ve+1,ve+1+pnt)-ve-1;
    	for(int i=1;i<=pnt;i++){
    		int x=ve[i];
    		if(t[x])q.push(x),v[x]=1,dis[x]=t[x];
    	}
    	while(!q.empty()){
    		int x=q.front();v[x]=0;q.pop();
    		for(int i=ls[x];i;i=a[i].next){
    			int y=a[i].to;
    			if(dis[x]+a[i].w<dis[y]){
    				dis[y]=dis[x]+a[i].w;
    				if(!v[y])q.push(y),v[y]=1;
    			}
    		}
    	}
    	return;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<n;i++){
    		int x,y;scanf("%d%d",&x,&y);
    		addl(x,y);addl(y,x);
    	}
    	dfs(1,1);
    	for(int j=1;j<=T;j++)
    		for(int i=1;i<=n;i++)
    			f[i][j]=f[f[i][j-1]][j-1];
    	cnt=tot=0;m++;
    	memset(ls,0,sizeof(ls));
    	memset(dis,0x3f,sizeof(dis));
    	for(int z=1;z<=m;z++){
    		int op,x;
    		if(z==m)op=2;
    		else scanf("%d%d",&op,&x);
    		if(op==1){
    			if(!t[x])p[++cnt]=x,t[x]=z;
    		}
    		else if(op==3)q[++qnt]=mp(x,z),p[++cnt]=x;
    		else{
    			sort(p+1,p+1+cnt,cmp);
    			cnt=unique(p+1,p+1+cnt)-p-1;
    			if(p[1]!=1)s[++top]=1;
    			for(int i=1;i<=cnt;i++)Ins(p[i]);
    			while(top>1)adde(s[top-1],s[top]),top--;
    			solve();
    			for(int i=1;i<=qnt;i++){
    				if(dis[q[i].first]<=q[i].second)puts("wrxcsd");
    				else puts("orzFsYo");
    			}
    			for(int i=1;i<=cnt;i++){
    				int x=p[i];
    				ls[x]=t[x]=0;dis[x]=1e9;
    			}
    			for(int i=1;i<=pnt;i++){
    				int x=ve[i];
    				ls[x]=t[x]=0;dis[x]=1e9;
    			}
    			cnt=qnt=top=tot=pnt=0;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    5.16
    4.29
    二十节 作业
    作业
    作业
    控件
    选择排序
    百钱买百鸡
    用颜色变色变换来调整图像的对比度和亮度
    图像的裁剪
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14608421.html
Copyright © 2020-2023  润新知