3011: [Usaco2012 Dec]Running Away From the Barn

2013年3月21日 20:03

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

RunID User Problem Result Memory Time Language Code_Length Submit_Time
370535 Liu_Ts 3011 Accepted 14804 kb 908 ms C++ 1039 B 2013-03-17 23:35:13

 

题意

给出以1号点为根的一棵有根树,问每个点的子树中与它距离小于L的点有多少个。

题解

dfs,对于每个点,打一个+1的标记,在从它到根的路径上,第一个大于>L的点打一个-1的标记,

则每个点的答案就是以它为根的子树的标记和。

官方题解写的倍增,我只会记录dfs序列,然后二分。

 

#include<cstring>
#include<cmath>
#include<climits>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=210000;
int v[N],next[N],head[N],n,k; ll len,w[N];
int f[N],stack[N],tot;ll s[N];
void build(int x,int y,ll z)
{
    v[++k]=y;w[k]=z;next[k]=head[x];head[x]=k;
}
 
int check(int r)
{
    ll l=1;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(s[stack[tot]]-s[stack[mid]]>len) l=mid;
        else r=mid-1;
    }
    return stack[l];
}
 
void dfs(int x,ll sum)
{
    stack[++tot]=x;s[x]=sum;
    f[x]+=1;f[check(tot)]-=1;
    for(ll i=head[x];i;i=next[i])
    {
        dfs(v[i],sum+w[i]);
        f[x]+=f[v[i]];
    }
    tot--;
}
 
int main()
{
    scanf("%d%lld",&n,&len);
    for(int i=2;i<=n;i++)
    {
        ll x,y;scanf("%d%lld",&x,&y);
        build(x,i,y);
    }
    s[0]=-ll(1e20);stack[++tot]=0;
    dfs(1,0);
    for(int i=1;i<=n;i++)
        printf("%d\n",f[i]);
}

Tags: bzoj usaco
评论(1) 阅读(1798)

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) 阅读(1055)

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) 阅读(913)

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)