现在的位置: 首页 > 综合 > 正文

模拟赛 藏宝图(时间限制:2s,空间限制:256MB)

2018年04月24日 ⁄ 综合 ⁄ 共 5039字 ⁄ 字号 评论关闭

题目描述

Czy发现了一张奇怪的藏宝图。图上有n个点,m条无向边。已经标出了图中两两之间距离dist。但是czy知道,只有当图刚好又是一颗树的时候,这张藏宝图才是真的。如果藏宝图是真的,那么经过点x的边的边权平均数最大的那个x是藏着宝物的地方。请计算这是不是真的藏宝图,如果是真的藏宝之处在哪里。

格式

输入数据第一行一个数T,表示T组数据。
对于每组数据,第一行一个n,表示藏宝图上的点的个数。
接下来n行,每行n个数,表示两两节点之间的距离。
输出一行或两行。第一行”Yes”或”No”,表示这是不是真的藏宝图。
若是真的藏宝图,第二行再输出一个数,表示哪个点是藏宝之处。

样例输入

2
3
0 7 9
7 0 2
9 2 0
3
0 2 7
2 0 9
7 9 0

样例输出

Yes
1
Yes
3

样例解释:第一棵树的形状是1--2--3。1、2之间的边权是7,2、3之间是2。
第二棵树的形状是2--1--3。2、1之间的边权是2,1、3之间是7。

数据范围

对于30%数据,n<=50,1<=树上的边的长度<=10^9。
对于50%数据,n<=600.
对于100%数据,1<=n<=2500,除30%小数据外任意0<=dist[i][j]<=10^9,T<=5

题解

我机房一位数据狂魔出的题目和数据,感觉非常不爽。
本题原题是codeforces上一次7题比赛中的D题,不过说是原题,还是加强了。

首先:第一问是cf原问题,解法是根据所给dist求出最小生成树,判断是否符合题意即可。注意:30%的数据会爆int,要开long long,还有据说cf原题可以用Kruskal卡过,出题人也卡掉了。但个人感觉prim好像更好想。

第二问是出题人自己加的。只要在做最小生成树时顺便记录一下即可。但是出题人之所以叫“数据狂魔”,是因为用prim有2个点还是会T。以下是我80分的代码,勉强卡进3s。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define ll long long
#define inf 1e18
using namespace std;
int T,n,tag;
ll sum[2502],dis[2502],map[2502][2502];
int ct[2502],from[2502],pd[2502];
int zz,head[2502],q[2502];
struct bian{int to,nx;ll v;} e[5002]; 
void init()
{
	scanf("%d",&n); tag=0;
	int i,j;
	for(i=1;i<=n;i++)
	for(j=1;j<=n;j++)
	   {scanf("%I64d",&map[i][j]);
	    if(i==j&&map[i][j]!=0) tag=-1;
	    if(i!=j&&map[i][j]==0) tag=-1;
	    if(i>j&&map[i][j]!=map[j][i]) tag=-1;
	   }
}
//------------------------------------------------------------------------------
void insert(int x,int y,ll z)
{
	zz++; e[zz].to=y; e[zz].v=z; e[zz].nx=head[x]; head[x]=zz;
	zz++; e[zz].to=x; e[zz].v=z; e[zz].nx=head[y]; head[y]=zz;
}
void prim()
{
	int i;
	for(i=0;i<=n;i++)
	   {dis[i]=inf; head[i]=0; ct[i]=0; sum[i]=0; from[i]=0; pd[i]=0;}
	dis[1]=0;
	int x,j;
	for(i=1;i<=n;i++)
	   {x=0;
	    for(j=1;j<=n;j++)
	       {if(!pd[j]&&dis[j]<dis[x]) x=j;}
	    pd[x]=1;
	    if(from[x])
	       {sum[x]+=map[from[x]][x]; sum[from[x]]+=map[from[x]][x];
		    ct[x]++; ct[from[x]]++;
		    insert(from[x],x,map[from[x]][x]);
		   }
	    for(j=1;j<=n;j++)
	       {
			if(!pd[j]&&dis[j]>map[x][j])
			   {dis[j]=map[x][j];
			    from[j]=x;
			   }
		   }
	   }
}
//------------------------------------------------------------------------------
bool bfs(int s)
{
	int i,x,p,t=0,w=1;
	q[0]=s; pd[s]=1;
	while(t<w)
	   {x=q[t]; t++;
	    for(i=head[x];i;i=e[i].nx)
	       {p=e[i].to;
		    if(pd[p]) continue;
		    dis[p]=dis[x]+e[i].v;
		    q[w]=p; w++; pd[p]=1;
		   }
	   }
	for(i=1;i<=n;i++)
	   {if(dis[i]!=map[s][i]) return false;}
	return true;
	
}
void check()
{
	int i,j;
	for(i=1;i<=n;i++)
	   {for(j=1;j<=n;j++) {dis[j]=0; pd[j]=0;}
	    if(!bfs(i)) {puts("No"); return ;}
	   }
	puts("Yes");
	int ans=1;
	for(i=2;i<=n;i++)
	   {if((double)(sum[i]/ct[i])>(double)(sum[ans]/ct[ans]))
	       ans=i;
	   }
	printf("%d\n",ans);
}
int main()
{
	freopen("treas.in","r",stdin);
	freopen("treas.out","w",stdout);
	scanf("%d",&T);
	while(T--)
	   {init();
	    if(tag==-1) {puts("No"); continue;}
	    prim(); check();
	   }
	return 0;
}

所以,想拿这20分,要加上一个堆优化prim,因为prim和dijkstra相似。至于判断生成树是否合法,O(n^2)就可以了,倍增反而更累更慢。本弱也想不出什么其他的优化了……由于不想背STL的写法,所以我还是手写堆,毕竟noip没开O2优化,不过还是很羡慕随手敲堆的大神。

还有……最坑爹的是,出题人要求随后两个点一定要加快速读入……

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define inf 1e15
#define ll long long
using namespace std;
int T,n,tag;
ll dis[2502],map[2502][2502],sum[2502];
int pd[2502],from[2502],ct[2502];
int zz,head[2502],q[2502],size;
struct bian{int to,nx;ll v;} e[6002];
struct DUI{int w;ll d;} dui[6250002];
ll read()
{
	ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    n=read(); tag=0;
    int i,j;
    for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
       map[i][j]=read();
    for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
       {if(map[i][j]==0&&i!=j) {tag=-1; return ;}
        if(i==j&&map[i][j]!=0) {tag=-1; return ;}
        if(map[i][j]!=map[j][i]) {tag=-1; return ;}
       }
}
void insert(int x,int y,ll z)
{
    zz++; e[zz].v=z; e[zz].to=y; e[zz].nx=head[x]; head[x]=zz;
    zz++; e[zz].v=z; e[zz].to=x; e[zz].nx=head[y]; head[y]=zz;
}
//-----------------------------------------------------------------------------
void heapfy(int x)
{
	int l,r,minx=x;
	l=x<<1; r=l+1;
	if(l<=size&&dui[l].d<dui[minx].d) minx=l;
	if(r<=size&&dui[r].d<dui[minx].d) minx=r;
	if(minx!=x)
	   {swap(dui[minx],dui[x]);
	    heapfy(minx);
	   }
}
void del()
{
	dui[1].w=dui[size].w; dui[1].d=dui[size].d; size--;
	if(size>0) heapfy(1);
}
void weih(int x)
{
	if(x<=1) return;
	int i=x/2;
	if(dui[i].d>dui[x].d)
	   {swap(dui[i],dui[x]); weih(i);}
}
void prim()
{
    int i,j,x;
    for(i=1;i<=n;i++)
	   {dis[i]=inf; head[i]=0; pd[i]=0;
	    from[i]=0; sum[i]=0; ct[i]=0;
	   }
    size=1; dui[1].w=1; dui[1].d=0; dis[1]=0;
    while(size>0)
       {x=dui[1].w; del();
	    if(pd[x]) continue;
	    //cout<<x<<endl;
	    pd[x]=1;
	    if(from[x])
		   {sum[from[x]]+=dis[x]; sum[x]+=dis[x];
		    ct[from[x]]++; ct[x]++;
			insert(from[x],x,dis[x]);
		   }
		for(i=1;i<=n;i++)
		   {if(!pd[i]&&dis[i]>map[x][i])
		       {dis[i]=map[x][i]; from[i]=x;
			    size++; dui[size].w=i; dui[size].d=dis[i]; weih(size);
			   }
		   }
	   }
}
void bfs(int x)
{
    dis[x]=0;
    q[0]=x;
    int now,i,t=0,w=1;
    while(t<w)
       {now=q[t]; t++;
        for(i=head[now];i;i=e[i].nx)
           {if(dis[e[i].to]==inf)
               {dis[e[i].to]=dis[now]+e[i].v;
                q[w]=e[i].to; w++;
               }
           }
       }
}
void check()
{
	int i,j;
	for(i=1;i<=n;i++)
       {for(j=1;j<=n;j++) dis[j]=inf;
        bfs(i);
        for(j=1;j<=n;j++)
           {if(dis[j]!=map[i][j])
               {printf("No\n"); return ;}
           }
       }
    printf("Yes\n");
    int x=1;
	for(i=2;i<=n;i++)
	   {if((double)sum[i]/ct[i]>(double)sum[x]/ct[x])
	       x=i;
	   }
	printf("%d\n",x);
}
int main()
{
    freopen("treas.in","r",stdin);
    freopen("treas.out","w",stdout);
	T=read();
    while(T--)
       {init();
	    if(tag==-1) {printf("No\n"); continue;}
	    if(n==1) {printf("Yes\n"); puts("1"); continue;}
	    prim(); check();
	   }
    return 0;
}

抱歉!评论已关闭.