1131: [POI2008]Sta

2013年3月21日 18:31

http://www.lydsy.com/JudgeOnline/problem.php?id=1131

RunID User Problem Result Memory Time Language Code_Length Submit_Time
372233 Liu_Ts 1131 Accepted 51588 kb 7000 ms C++ 1058 B 2013-03-20 10:52:25

 

题意

给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大

题解

感觉会爆栈,写的BFS,代码太渣了,

cost[i]记以这点为根的深度之和,sum[i]为i的子树大小。

 

#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000001,M=N*2;
int n,u[M],v[M],next[M],head[N],k;
int q[N],fa[N];
long long sum[N],cost[N];
 
void add(int x,int y)
{
    u[++k]=x;v[k]=y;next[k]=head[x];head[x]=k;
    u[++k]=y;v[k]=x;next[k]=head[y];head[y]=k;
}
 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);
    }
    int l=0,r=1;q[1]=1;fa[1]=0;
    while(l<r)
    {
        int now=q[++l];
        for(int i=head[now];i;i=next[i])
            if(fa[now]!=v[i])
            {
                fa[v[i]]=now;
                q[++r]=v[i];
            }
    }
    for(int i=r;i>0;i--)
    {
        sum[q[i]]+=1;
        sum[fa[q[i]]]+=sum[q[i]];
        cost[fa[q[i]]]+=cost[q[i]]+sum[q[i]];
    }
    long long MAX=cost[1];int ans=1;
    for(int i=2;i<=r;i++)
    {
        cost[q[i]]=cost[fa[q[i]]]-(sum[q[i]]<<1)+n;
        if((cost[q[i]]>MAX)||(cost[q[i]]==MAX && q[i]<ans))
            MAX=cost[q[i]],ans=q[i];
    }
    printf("%d\n",ans);
}

Tags: bzoj poi
评论(0) 阅读(1054)

1706: [usaco2007 Nov]relays

2013年3月21日 17:41

http://www.lydsy.com/JudgeOnline/problem.php?id=2213

RunID User Problem Result Memory Time Language Code_Length Submit_Time
373294 Liu_Ts 1706 Accepted 936 kb 272 ms C++ 1146 B 2013-03-21 17:13:56

 

题目大意:

在一个无向图中有T条边,每个点至少和两条边相连,每条边有一个长度,询问从给定起点到给定终点的包含N条边的路最短是多长。

问题规模:

1<=N<=1000000000, 1<=T<=100

 

这题我了解到3个方法,都很神牛,很有启发。

1.基于倍增的floyd

官方的解法。假设我们已经知道了两点间经过k条边的最短路,就可以利用floyd求出两点间经过k*2条边的最短路,于是我们可以依次求出经过1,2,4,8,16...条边的最短路,它们正好可以组成任何二进制数,我们只要把N分解成这些的和,再用floyd求一遍就可以了。复杂度O(T^3 logN)。

2.矩阵乘法思想

看到这题的数据应该很多人想到矩阵乘法快速幂,但矩阵乘法表示的是乘积的和,如果是要求经过N条边的路径数就可以直接这么做了,无奈之下只好放弃了这个想法。但其实我们可以尝试定义一个新的矩阵乘法,使它可以表示我们需要的结果。

原始的矩阵乘法是这样的:如果C=A*B 那么

 

我们可以定义C=A@B


于是样例的结果就可以表示为


可以证明运算@是满足结合律的,所以也可以用快速幂求出。

这个方法时间复杂度跟floyd是一样的O(T^3 logN),而且写起来也差不多,以至于我认为它们本质上是一种方法。

3.部分贪心思想

这个是GYH神牛的论文《部分贪心思想在信息学竞赛中的应用》里的,核心有两点:

1.最短路径一定在一条较短边(这条路上的最短边)上来回走了很多次

2.路径上的其他边至多经过两次

证明详见论文。于是我们只需算出起点和终点到每个点经过2*T条边的最短路,然后枚举经过很多次的边,就可以在O(T^2)的时间内解决了。

 

题解转自http://hi.baidu.com/lilymona/item/3b401d86d22c6fd6d0f8cdf5

代码是写了第二种办法。

 

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
using namespace std;
const int N=102,M=1005;
int n,e,s,t,vis[M],tot;

struct matrix 
{
	int a[N][N];
	int *operator [](int x) { return a[x];}
	void clear()
	{
		memset(a,63,sizeof(a));
	}
	matrix operator*(matrix b) 
	{
		matrix c;
		memset(c.a,63,sizeof(c.a));
		for(int i=1;i<=tot;++i)
			for(int j=1;j<=tot;++j)
				for(int k=1;k<=tot;++k)
					c[i][j]=min(c[i][j],a[i][k]+b[k][j]);
		return c;
	}
	matrix operator^(int n) 
	{
		matrix b;b=*this;--n;
		while(n) 
		{
			if(n&1) b=b**this;
			*this=*this**this;
			n/=2;
		}
		return b;
	}
}a;

int get(int x)
{
	if(!vis[x])	vis[x]=++tot;
	return vis[x];
}

int main()
{
	scanf("%d%d%d%d",&n,&t,&s,&e);
	a.clear();
	memset(vis,0,sizeof(vis));tot=0;
	for(int i=0;i<t;i++)
	{
		int z,x,y;scanf("%d%d%d",&z,&x,&y);
		x=get(x);y=get(y);
		a[x][y]=a[y][x]=z;
	}
	s=get(s);e=get(e);
	matrix ans=a^n;
	printf("%d\n",ans[s][e]);
}

Tags: bzoj usaco
评论(0) 阅读(911)

1054: [HAOI2008]移动玩具

2013年3月20日 10:21

http://www.lydsy.com/JudgeOnline/problem.php?id=1054

RunID User Problem Result Memory Time Language Code_Length Submit_Time
371808 Liu_Ts 1054 Accepted 1316 kb 16 ms C++ 1000 B 2013-03-19 19:10:37

 

大意

在一个4*4的方框内摆放了若干个相同的玩具,将这些玩具重新摆放成为理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具。求最小移动次数。

题解

暴力BFS,因为格子很少,直接用位运算

萌神的写法非常厉害,看代码吧。

 

#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=65536,
dx[24]={15,14,13,15,14,13,12,11,10,9,11,10,9,8,7,6,5,7,6,5,4,3,2,1},
dy[24]={14,13,12,11,10,9,8,10,9,8,7,6,5,4,6,5,4,3,2,1,0,2,1,0};
int q[N],d[N],S,T;

int main()
{
	memset(d,127,sizeof(d));
	for(int i=0;i<4;i++,getchar())
		for(int j=0;j<4;j++)
			S=(S<<1)+getchar()-'0';
	getchar();
	for(int i=0;i<4;i++,getchar())
		for(int j=0;j<4;j++)
			T=(T<<1)+getchar()-'0';
	if(S==T){puts("0");return 0;}
	d[S]=0;int l=0,r=1;q[1]=S;
	while(l<r)
	{
		int now=q[++l];
		for(int i=0;i<24;i++)
		{
			int x=now&(1<<dx[i]),y=now&(1<<dy[i]);
			int next=now-x-y;
			if(x)	next+=1<<dy[i];
			if(y)	next+=1<<dx[i];
			if(d[next]>d[now]+1)
			{
				d[next]=d[now]+1;
				q[++r]=next;
				if(next==T)
				{
					printf("%d\n",d[T]);
					return 0;
				}
			}
		}
	}
}

Tags: bzoj HAOI
评论(0) 阅读(1031)

1742: [Usaco2005 nov]Grazing on the Run

2013年3月19日 17:36

http://www.lydsy.com/JudgeOnline/problem.php?id=1742

RunID User Problem Result Memory Time Language Code_Length Submit_Time
371695 Liu_Ts 1742 Accepted 8796 kb 96 ms C++ 757 B 2013-03-19 16:58:10

 

大意

数轴上有N个点,起点为额外的P点,在某个点被到达之前,这个点每单位时间会消费1点费用,到达后不再产生费用。求遍历所有点花费的最小费用。

题解

区间DP,

L(a,b) 表示已经走完a到b,且当前在a点的最小花费;

R(a,b) 表示已经走完a到b,且当前在a点的最小花费;

则:

L(a,b) = min( L(a+1,b) + |x(a)-x(a+1)|, R(a+1,b) + |x(a)-x(b)| )

R(a,b) = min( R(a,b-1) + |x(b)-x(b-1)|, L(a,b-1) + |x(b)-x(a)| )

Ans = min(L(1,n), R(1,n))
 
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1011;
int n,a[N],L[N][N],R[N][N];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	a[++n]=0;
	sort(a+1,a+1+n);
	int t=lower_bound(a+1,a+1+n,0)-a;
	memset(L,60,sizeof(L));
	memset(R,60,sizeof(R));
	L[t][t]=R[t][t]=0;
	for(int l=2;l<=n;l++)
		for(int i=1;i<=n;i++)
		{
			int j=i+l-1;
			if(j>n)	continue;
			L[i][j]=min(L[i+1][j]+(n-l+1)*(a[i+1]-a[i]),
						R[i+1][j]+(n-l+1)*(a[j]-a[i]));
			R[i][j]=min(L[i][j-1]+(n-l+1)*(a[j]-a[i]),
						R[i][j-1]+(n-l+1)*(a[j]-a[j-1]));
		}
	printf("%d\n",min(L[1][n],R[1][n]));
}

 

Tags: bzoj usaco
评论(0) 阅读(928)

1638: [Usaco2007 Mar]Cow Traffic

2013年3月19日 15:58

http://www.lydsy.com/JudgeOnline/problem.php?id=1638

RunID
User
Problem
Result
Memory
Time
Language
Code_Length
Submit_Time
371552
Accepted
1684 kb
32 ms
985 B
2013-03-19 15:20:26

 

描述

给定一个有向无环图,起点为所有入度为0的点,终点为N。题目大意

计算所有路径通过某条边的最大次数。

样例:

 1   4
  \ / \
   3   6 -- 7
  / \ /
 2   5
通向奶牛宿舍的所有路径: 
1 3 4 6 7
1 3 5 6 7
2 3 4 6 7
2 3 5 6 7
所以 6-->7 的经过次数最多,为4.
 

题解

把所有起点入队,正向BFS,求出所有起点到每个点的路径数f[i];

把终点入队,反向BFS,求出终点到每个点的路径数g[i];

ans=max{g[u]*f[v]} | (u,v)∈E;

 

#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5001,M=50001;
int v[M],next[M],head[N],in[N],k;
int x[M],y[M],f[N],g[N],*d,q[N],n,m;
void add(int x,int y)
{
	v[++k]=y;next[k]=head[x];head[x]=k;
	in[y]++;
}
void bfs(int d[])
{
	memset(d,0,sizeof(d));
	int l=0,r=0;
	for(int i=1;i<=n;i++)
		if(!in[i])	q[++r]=i,d[i]=1;
	while(++l<=r)
		for(int i=head[q[l]];i;i=next[i])
		{
			d[v[i]]+=d[q[l]];
			if(!--in[v[i]])	q[++r]=v[i];
		}
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		if(x>y)	swap(x[i],y[i]);
		add(x[i],y[i]);
	}
	bfs(f);
	memset(in,0,sizeof(in));
	memset(head,0,sizeof(head));k=0;
	for(int i=0;i<m;i++)	add(y[i],x[i]);
	bfs(g);
	int ans=0;
	for(int i=0;i<m;i++)
		ans=max(ans,f[x[i]]*g[y[i]]);
	printf("%d\n",ans);
}

 

 
 
 

 

 
 

Tags: usaco bzoj
评论(0) 阅读(701)