• [GYM102798F]Skeleton Dynamization


    VII.[GYM102798F]Skeleton Dynamization

    神题。

    首先,我们考虑若我们确定有一条边 ((u,v)),是连接层 (i) 和层 (i+1) 上对应点的边,有无办法建出整个分层图出来?

    答案是有的。首先,我们先跑两遍bfs求出所有点到 (u)(v) 的距离。然后,稍加观察可以发现,(1sim i) 层里的点,到 (u) 的距离更近,其余点则反之。因此我们可以根据上述算法将整张图分作两半。之后,所有处在前一半中,且与后一半中点有边的点,均来自第 (i) 层;所有处在后一半且与前一半中点有边的点均来自第 (i+1) 层。

    假如我们已经确定第 (i) 层所有节点和第 (i+1) 层所有节点,则我们可以直接确定出 (i+2) 层所有节点——它们是与 (i+1) 层中点有边,且不来自 (i)(i+1) 层的所有节点。类似地,我们可以确定 (i+1sim n) 层所有节点。同理,(1sim i-1) 层的节点也可以类似确定。如此确定后,只需check此分层图是否符合要求即可(层层间仅所有对应点有且仅有一条边;每层的图同构)。至于如何check图同构,因为同构的点都已经对应了,直接check度数是否相等即可。

    考虑上述算法的复杂度——其全程只进行了对点和边的遍历,复杂度为 (O(n+m))。于是我们下面需要找到一种合适的枚举边 ((u,v)) 的方式。

    显然,对于一个点,其必有一条边连到下一层中某个点。这意味着,我们可以通过遍历与一点相邻的所有边check进而check所有的划分方式。

    考虑所有点中度数最少的点——这一值最大是 (O(sqrt{m})) 级别的——我们枚举其所有边check,最大复杂度为 (Big((n+m)sqrt{m}Big)),可以通过。

    需要注意的是,度数最少的点一定是位于分层图中的第一层或最后一层中(但实际上可以直接将其当作是在第一层),故善用这一性质可以减少很多讨论量。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100100;
    int n,m,dis1[N],dis2[N],lay[N],num,deg[N],mnpos,mxano,mxlay,pre[N];
    queue<int>q;
    vector<int>v[N];
    void bfs(int S,int *d){
    	q.push(S),d[S]=0;
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(auto y:v[x])if(d[y]==-1)d[y]=d[x]+1,q.push(y);
    	}
    }
    vector<int>r[N];
    void functionset(int id){
    	num++;
    	for(auto x:r[id])for(auto y:v[x])if(!lay[y])lay[y]=num,r[num].push_back(y);else if(lay[y]==lay[x])deg[x]++;
    	if(r[num].empty())num--;
    }
    bool checkedge(int U,int V){
    	for(int i=1;i<=num;i++)r[i].clear();num=2;
    //	printf("%d
    ",V);
    	memset(dis2,-1,sizeof(dis2)),bfs(V,dis2),memset(lay,0,sizeof(lay)),memset(deg,0,sizeof(deg));
    //	for(int i=1;i<=n;i++)printf("%d ",dis1[i]);puts("");
    //	for(int i=1;i<=n;i++)printf("%d ",dis2[i]);puts("");
    	for(int x=1;x<=n;x++){
    //		printf("%d:%d %d
    ",x,dis1[x],dis2[x]);
    		if(dis1[x]==dis2[x])return false;
    		for(auto y:v[x]){
    			if((dis1[x]<dis2[x])==(dis1[y]<dis2[y]))continue;
    			if(dis1[x]<dis2[x])r[lay[x]=1].push_back(x);
    			else r[lay[x]=2].push_back(x);
    		}
    	}
    	for(int i=1;i<=num;i++)functionset(i);
    //	for(int i=1;i<=n;i++)printf("%d ",lay[i]);puts("");
    //	for(int i=1;i<=n;i++)printf("%d ",deg[i]);puts("");
    	for(int i=1;i<=num;i++)for(auto x:r[i]){
    		int pr=0,nx=0;
    		for(auto y:v[x]){
    			if(lay[y]==lay[x])continue;
    			if(lay[y]==lay[x]+1){
    				if(!nx)nx=y;
    				else return false;
    				continue;
    			}
    			if(lay[y]==lay[x]-1){
    				if(!pr)pr=y;
    				else return false;
    				continue;
    			}
    			return false;//has a connetion beyond adjacent layers
    		}
    //		printf("%d->%d->%d
    ",pr,x,nx);
    		if(lay[x]<num&&(!nx||deg[nx]!=deg[x]))return false;
    		if(lay[x]>1&&(!pr||deg[pr]!=deg[x]))return false;
    		if(lay[x]>1)pre[x]=pre[pr];
    		else pre[x]=x;
    	}
    	return true;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x),deg[x]++,deg[y]++;
    	mnpos=1;
    	for(int i=2;i<=n;i++)if(deg[i]<deg[mnpos])mnpos=i;
    //	printf("%d
    ",mnpos);
    	memset(dis1,-1,sizeof(dis1)),bfs(mnpos,dis1);
    	for(auto x:v[mnpos]){
    		if(!checkedge(mnpos,x))continue;
    		if(mxlay<num)mxlay=num,mxano=x;
    	}
    	if(!mxlay){
    		printf("%d %d
    ",1,n);
    		for(int i=1;i<=n;i++)printf("%d ",i);puts("");
    		return 0; 
    	}
    	checkedge(mnpos,mxano);
    	printf("%d %d
    ",mxlay,n/mxlay);
    	for(int i=1;i<=mxlay;i++){
    		sort(r[i].begin(),r[i].end(),[](int x,int y){return pre[x]<pre[y];});
    		for(auto j:r[i])printf("%d ",j,pre[j]);
    		puts("");
    	}
    	return 0;
    }
    

  • 相关阅读:
    灌溉 最小生成树
    queue的应用uva540
    大数相加
    对于jquery实现原理的浅谈
    div+CSS实现页面的布局要点记录
    spring注解注入的学习
    jsp内置对象学习记录
    web应用的乱码解决
    新发现:排序算法时间复杂度只有O(3n),命名为"wgw"排序法
    java编写的Http协议的多线程下载器
  • 原文地址:https://www.cnblogs.com/Troverld/p/14621853.html
Copyright © 2020-2023  润新知