zoj3511 杂/树状数组

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3511

 

Contest: ZOJ Monthly, July 2011

题意:一个蛋糕为一个凸包,边上n(10000)个点,编号1--n,每次以i,j为线切蛋糕,看m刀过后剩下的边数最多的那一块的边数。。。(所有切的线不会从中间相交)

 

分析:网上有人直接用vector暴力,每次在当前所有集合中找同时存在i和j的,然后将这个集合划分为两个保存。。。

另思路:首先按照每一刀将整个集合分成的两个集合的点数少的那一边的点数从小到大排序,然后每次从前往后切,(这样就保证了切到每一刀的时候这小的一边有切的地方多余的点都被灭掉了),每次找小的这一边的边数与ans比较更新,然后将这两个点的这一边的所有点删掉,用树状数组维护点的个数。。。

100ms过,当然最后发现:删点的过程最坏情况还是n^2的复杂度。。。还没想到怎么优化。。

优化:其实优化很简单,加一个next数组记录某个点的下一个点是谁即可,每次灭掉一些点之后将首点的next指向尾点即可,这样,时间复杂度就标准的nlog(n)了。。。30ms

优化1:从用next数组时发现其实可以不用树状数组了。。。orz。。反正始终要通过next扫描x和y之间的可行点,则直接计数即可。。。20ms。。。 

优化2:其实可以不用next数组,直接用梳妆数组在x或y的位置减去他们之间的点的个数即可。。。20ms。。

 

代码:

优化2:20ms

 
#include<iostream>
#include<stdio.h>
#include<memory.h>
#include<algorithm>
using namespace std;

const int N=20010;
int a[N], sum[N], n, nn, m, ans;
struct node 
{
	int x, y;
}b[N];

void update(int i, int v)
{
	while(i<=nn)
	{
		sum[i] += v;
		i += i&(-i);
	}
}

int query(int i)
{
	int tmp = 0;
	while(i>0)
	{
		tmp += sum[i];
		i -= i&(-i);
	}
	return tmp;
}

int _min(int x, int y)
{
	return x<y?x:y;
}

int cmp(const node &a, const node &b)
{
	int i, j;
	i = _min( (a.x-a.y+n)%n, (a.y-a.x+n)%n );
	j = _min( (b.x-b.y+n)%n, (b.y-b.x+n)%n );
	return i<j;
}

int main()
{
	int i, j, tmp1, tmp2;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		if(m==0)
		{
			printf("%d\n", n);
			continue;
		}
		nn = n*2;
		for(i=0; i<m; i++)
		{
			scanf("%d%d", &b[i].x, &b[i].y);
			if(b[i].x>b[i].y)
				swap(b[i].x, b[i].y);
		}
		sort(b, b+m, cmp);
		for(i=1; i<=nn; i++)
			sum[i] = 0;	
		for(i=1; i<=nn; i++)
		{
			update(i, 1);
			a[i] = 1;
		}

		ans = 0;
		for(i=0; i<m-1; i++) //对前面所有切线,只灭掉点数较短的一边。。。
		{
			tmp1 = b[i].y-b[i].x;
			tmp2 = b[i].x+n-b[i].y;
			if(tmp1<tmp2)
			{
				tmp1 = query(b[i].y)-query(b[i].x-1);
				if(tmp1>ans)
					ans = tmp1;
				update(b[i].x, -(tmp1-2));
				update(b[i].x+n, -(tmp1-2));
			}
			else
			{
				tmp2 = query(b[i].x+n)-query(b[i].y-1);
				if(tmp2>ans)
					ans = tmp2;
				update(b[i].y, -(tmp2-2));
			}
			
		} //for
		{//最后一个特殊处理
			i = m-1;
			tmp1 = query(b[i].y)-query(b[i].x-1);
			tmp2 = query(b[i].x+n)-query(b[i].y-1);
			if(tmp1>ans)
				ans = tmp1;
			if(tmp2>ans)
				ans = tmp2;
		}
		printf("%d\n", ans);
	}//while
	
	return 0;
}



 

 

代码:

优化1:20ms

#include<iostream>
#include<stdio.h>
#include<memory.h>
#include<algorithm>
using namespace std;

const int N=20010;
int n, nn, m, ans;
int next[N];
struct node 
{
	int x, y;
}b[N];

int _min(int x, int y)
{
	return x<y?x:y;
}

int cmp(const node &a, const node &b)
{
	int i, j;
	i = _min( (a.x-a.y+n)%n, (a.y-a.x+n)%n );
	j = _min( (b.x-b.y+n)%n, (b.y-b.x+n)%n );
	return i<j;
}

int main()
{
	int i, j, tmp1, tmp2;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		if(m==0)
		{
			printf("%d\n", n);
			continue;
		}
		nn = n*2;
		for(i=0; i<m; i++)
		{
			scanf("%d%d", &b[i].x, &b[i].y);
			if(b[i].x>b[i].y)
				swap(b[i].x, b[i].y);
		}
		sort(b, b+m, cmp);
		for(i=1; i<=nn; i++)
			next[i] = i+1;
		ans = 0;
		for(i=0; i<m-1; i++) //对前面m-1的每一刀,只统计和灭掉点数较短的一边。。。
		{
			tmp1 = b[i].y-b[i].x;
			tmp2 = b[i].x+n-b[i].y;
			if(tmp1<tmp2)
			{
				tmp1 = 0;
				for(j=next[b[i].x]; j<b[i].y; j=next[j])
					tmp1++;
				if(tmp1>ans)
					ans = tmp1;
				next[b[i].x] = b[i].y;
				next[b[i].x+n] = b[i].y+n;
			}
			else
			{
				tmp2 = 0;
				for(j=next[b[i].y]; j<b[i].x+n; j=next[j])
					tmp2++;
				if(tmp2>ans)
					ans = tmp2;
				next[b[i].y] = b[i].x+n;
			}
			
		} //for
		{//最后一刀特殊处理
			tmp1 = 0;
			i = m-1;
			for(j=next[b[i].x]; j<b[i].y; j=next[j])
				tmp1++;
			tmp2 = 0;
			for(j=next[b[i].y]; j<b[i].x+n; j=next[j])
				tmp2++;
			if(tmp1>ans)
				ans = tmp1;
			if(tmp2>ans)
				ans = tmp2;
		}
		printf("%d\n", ans+2);
	}//while
	
	return 0;
}


 

 

代码:

优化后:30ms

#include<iostream>
#include<stdio.h>
#include<memory.h>
#include<algorithm>
using namespace std;

const int N=20010;
int a[N], sum[N], n, nn, m, ans;
int next[N];
struct node 
{
	int x, y;
}b[N];

void update(int i, int v)
{
	while(i<=nn)
	{
		sum[i] += v;
		i += i&(-i);
	}
}

int query(int i)
{
	int tmp = 0;
	while(i>0)
	{
		tmp += sum[i];
		i -= i&(-i);
	}
	return tmp;
}

int _min(int x, int y)
{
	return x<y?x:y;
}

int cmp(const node &a, const node &b)
{
	int i, j;
	i = _min( (a.x-a.y+n)%n, (a.y-a.x+n)%n );
	j = _min( (b.x-b.y+n)%n, (b.y-b.x+n)%n );
	return i<j;
}

int main()
{
	int i, j, tmp1, tmp2;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		nn = n*2;
		for(i=0; i<m; i++)
		{
			scanf("%d%d", &b[i].x, &b[i].y);
			if(b[i].x>b[i].y)
				swap(b[i].x, b[i].y);
		}
		sort(b, b+m, cmp);
		for(i=1; i<=nn; i++)
			sum[i] = 0;	
		for(i=1; i<=nn; i++)
		{
			update(i, 1);
			a[i] = 1;
			next[i] = i+1;
		}

		ans = 0;
		for(i=0; i<m-1; i++) //对前面所有切线,只灭掉点数较短的一边。。。
		{
			tmp1 = b[i].y-b[i].x;
			tmp2 = b[i].x+n-b[i].y;
			//tmp1 = query(b[i].y)-query(b[i].x-1);
			//tmp2 = query(b[i].x+n)-query(b[i].y-1);
			if(tmp1<tmp2)
			{
				tmp1 = query(b[i].y)-query(b[i].x-1);
				if(tmp1>ans)
					ans = tmp1;
				for(j=next[b[i].x]; j<b[i].y; j=next[j])
				{
					if(a[j]!=0)
					{
						update(j, -1);
						a[j] = 0;
					}
				}
				next[b[i].x] = b[i].y;
				for(j=next[b[i].x+n]; j<b[i].y+n; j=next[j])
				{
					if(a[j]!=0)
					{
						update(j, -1);
						a[j] = 0;
					}
				}
				next[b[i].x+n] = b[i].y+n;
			}
			else
			{
				tmp2 = query(b[i].x+n)-query(b[i].y-1);
				if(tmp2>ans)
					ans = tmp2;
				for(j=next[b[i].y]; j<b[i].x+n; j=next[j])
				{
					if(a[j]!=0)
					{
						update(j, -1);
						a[j] = 0;
					}
					next[b[i].y] = b[i].x+n;
				}
			}
			
		} //for
		{//最后一个特殊处理
			i = m-1;
			tmp1 = query(b[i].y)-query(b[i].x-1);
			tmp2 = query(b[i].x+n)-query(b[i].y-1);
			if(tmp1>ans)
				ans = tmp1;
			if(tmp2>ans)
				ans = tmp2;
		}
		printf("%d\n", ans);
	}//while
	
	return 0;
}



 

 未优化:100ms

#include<iostream>
#include<stdio.h>
#include<memory.h>
#include<algorithm>
using namespace std;

const int N=20010;
int a[N], sum[N], n, nn, m, ans;
struct node 
{
	int x, y;
}b[N];

void update(int i, int v)
{
	while(i<=nn)
	{
		sum[i] += v;
		i += i&(-i);
	}
}

int query(int i)
{
	int tmp = 0;
	while(i>0)
	{
		tmp += sum[i];
		i -= i&(-i);
	}
	return tmp;
}

int _min(int x, int y)
{
	return x<y?x:y;
}

int cmp(const node &a, const node &b)
{
	int i, j;
	i = _min( (a.x-a.y+n)%n, (a.y-a.x+n)%n );
	j = _min( (b.x-b.y+n)%n, (b.y-b.x+n)%n );
	return i<j;
}

int main()
{
	int i, j, tmp1, tmp2;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		nn = n*2;
		for(i=0; i<m; i++)
		{
			scanf("%d%d", &b[i].x, &b[i].y);
			if(b[i].x>b[i].y)
				swap(b[i].x, b[i].y);
		}
		sort(b, b+m, cmp);
		for(i=1; i<=nn; i++)
			sum[i] = 0;	
		for(i=1; i<=nn; i++)
		{
			update(i, 1);
			a[i] = 1;
		}

		ans = 0;
		for(i=0; i<m-1; i++) //对前面所有切线,只灭掉点数较短的一边。。。
		{                            tmp1 = b[i].y-b[i].x;                            tmp2 = b[i].x+n-b[i].y;
			//tmp1 = query(b[i].y)-query(b[i].x-1);
			//tmp2 = query(b[i].x+n)-query(b[i].y-1);
			if(tmp1<tmp2)
			{                                    tmp1 = query(b[i].y)-query(b[i].x-1);
				if(tmp1>ans)
					ans = tmp1;
				for(j=b[i].x+1; j<b[i].y; j++)
				{
					if(a[j]!=0)
					{
						update(j, -1);
						a[j] = 0;
					}
				}
				for(j=b[i].x+n+1; j<b[i].y+n; j++)
				{
					if(a[j]!=0)
					{
						update(j, -1);
						a[j] = 0;
					}
				}
			}
			else
			{                                    tmp2 = query(b[i].x+n)-query(b[i].y-1);
				if(tmp2>ans)
					ans = tmp2;
				for(j=b[i].y+1; j<b[i].x+n; j++)
				{
					if(a[j]!=0)
					{
						update(j, -1);
						a[j] = 0;
					}
				}
			}
			
		} //for
		{//最后一个特殊处理
			i = m-1;
			tmp1 = query(b[i].y)-query(b[i].x-1);
			tmp2 = query(b[i].x+n)-query(b[i].y-1);
			if(tmp1>ans)
				ans = tmp1;
			if(tmp2>ans)
				ans = tmp2;
		}
		printf("%d\n", ans);
	}//while
	
	return 0;
}

你可能感兴趣的:(zoj3511 杂/树状数组)