HUT 线段树练习 部分题解

Posted 此剑之势愈斩愈烈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HUT 线段树练习 部分题解相关的知识,希望对你有一定的参考价值。

F-poj 2886

这题去年月神给我们14级的抓过。然而比较偏数学。

题意大概是n个小朋友围成一圈,每个小朋友手里都有一张卡片,卡片上有个数字a[i]。

从第k个小朋友开始,第k个小朋友出圈,然后让他的左手方向的第a[k]个小朋友出圈。然后这个小朋友又根据规则让另一个小朋友出圈。

第p个出圈的小朋友拿到的糖果数目为p的因子个数,问谁拿到了最多的糖果

如果我们从1到n,逐个判断它的因子数目有多少个,效率非常低。其实我们可以通过规律直接dfs出小于等于n的,因子个数最多的数

因为要使因子个数尽量的多,该数应该满足如下性质,即可以表示为一系列连续质数的积,形如n=2^x1*3^x2*5^x3......

于是我们根据这个思路进行dfs。然后我们就得到是第几个出圈的人拿到最多的糖果啦。

之后我们要用线段树,求得拿到最多糖果的人是谁。

线段树维护区间当前的人数,然后把查询左手方向的第a[k]个小朋友,转化为查询剩余数组的第k的小朋友(取相对位置),就能直接在线段树中查询了

这里有关于反素数比较详细的介绍http://blog.csdn.net/ACdreamers/article/details/25049767

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500008;
int p[16]={2,3,5,7,11,13,17,19,23,29,31,37}; 
int mx,nx;
void dfs(int num,int x,int y,int lim)
{
	if(num>=9) 	return;
	if(mx<x)
	{
		mx=x;
		nx=y;
	}
	if(mx==x&&nx>y)
		nx=y; 
	int i;
	for(i=1;i<=10;i++)
	{
		if(lim<y*p[num])	break; 
		y*=p[num];
		dfs(num+1,x*(i+1),y,lim);
	}
}		 
struct fuck{
	int x;
	char s[15];
}f[maxn]; 
int a[maxn<<2]; 
void build(int x,int y,int c)
{
	if(x==y)
	{
	 	a[c]=1;
		 return;
	}
	int mid=(x+y)>>1;
	build(x,mid,c<<1);
	build(mid+1,y,c<<1|1);
	a[c]=a[c<<1]+a[c<<1|1];
}
int update(int m,int x,int y,int c)
{
	 a[c]--;
	if(x==y)
	{
	  	return x;
	}
	int mid=(x+y)>>1;
	if(m<=a[c<<1])	return update(m,x,mid,c<<1);
	else	return update(m-a[c<<1],mid+1,y,c<<1|1);
}
int main()
{
	int i,j,n,m;
	while(scanf("%d%d",&n,&m)==2)
	{
		mx=nx=1;
		dfs(0,1,1,n);
		for(i=1;i<=n;i++)
		 	scanf("%s%d",f[i].s,&f[i].x);
		build(1,n,1); 
		int k=n;
		int shit;
		for(i=1;i<=nx;i++)
		{
			k--;
			shit=update(m,1,n,1); 
			if(k<=0) 	break;
			if(f[shit].x>0) 
				m=(m-1+f[shit].x-1)%k+1;
			else
				m=((m+f[shit].x-1)%k+k)%k+1; 
		}
		printf("%s %d\\n",f[shit].s,mx); 
	}
	return 0;
}

 H - Mayor\'s posters

找一找可以发现别人转载的杭电老队长博客,里面有这题的详细介绍,关于离散化也有详细的讲解

http://www.cnblogs.com/Mu-Tou/archive/2011/08/11/2134427.html

I-hdu 1540 Tunnel Warfare

有可以看做一条线的村庄互相用地道连接。敌人会破坏一些村庄。八路军可以修好前一个被破坏的村庄。询问第k个村庄直接或间接连接了多少个村子。

区间合并问题。我的做法直接开两颗线段树。一颗存区间内最左边的,没有被破坏的村庄编号。一颗存区间内最右边的,没有被破坏的村庄编号,查询两次,将其相加即是答案。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=50008;
const int INF=0x7f7f7f7f;
int a[maxn<<4];
int b[maxn<<4];
int st[maxn];
int n;
void build(int x,int y,int c)
{
	if(x==y)
	{
		a[c]=n+1;
		b[c]=0;
		return;
	}
	int mid=(x+y)>>1;
	build(x,mid,c<<1);
	build(mid+1,y,c<<1|1);
	a[c]=min(a[c<<1],a[c<<1|1]);
	b[c]=max(b[c<<1],b[c<<1|1]);
}
void update(int sx,int sy,int x,int y,int c)
{
	if(x==y)
	{
		if(sy==0)
		{
			b[c]=a[c]=x;
		}
		else
		{
			b[c]=0;a[c]=n+1;
		}
		return;
	}
	int mid=(x+y)>>1;
	if(sx<=mid)	update(sx,sy,x,mid,c<<1);
	else	update(sx,sy,mid+1,y,c<<1|1);
	a[c]=min(a[c<<1],a[c<<1|1]);
	b[c]=max(b[c<<1],b[c<<1|1]);
}
int yquery(int sx,int sy,int x,int y,int c)
{
	if(sx<=x&&y<=sy)
	{
		return a[c];
	}
	int mid=(x+y)>>1;
	int ans=n+1;
	if(sx<=mid)	ans=min(ans,yquery(sx,sy,x,mid,c<<1));
	if(sy>mid)	ans=min(ans,yquery(sx,sy,mid+1,y,c<<1|1));
	return ans;	
}
int xquery(int sx,int sy,int x,int y,int c)
{
	if(sx<=x&&y<=sy)
	{
		return b[c];
	}
	int mid=(x+y)>>1;
	int ans=0;
	if(sx<=mid)	ans=max(ans,xquery(sx,sy,x,mid,c<<1));
	if(sy>mid)	ans=max(ans,xquery(sx,sy,mid+1,y,c<<1|1));
	return ans;	
}
bool query(int sx,int x,int y,int c)
{
	if(x==y)
	{
		if(a[c]==x)	return true;
		return false;
	}
	int mid=(x+y)>>1;
	if(sx<=mid)	return query(sx,x,mid,c<<1);
	else	return query(sx,mid+1,y,c<<1|1);
}
int main()
{
	int i,j,m;
	char s[5];
	while(scanf("%d%d",&n,&m)==2)
	{
		build(1,n,1);
		int u;
		int top=0;
		while(m--)
		{
			scanf("%s",s);
			if(s[0]==\'R\')
			{
				update(st[--top],1,1,n,1);
			}
			else
			{
				scanf("%d",&u);
				if(s[0]==\'D\')
				{
					update(u,0,1,n,1);
					st[top++]=u;
				}
				else
				{
					int bitch;
					if(query(u,1,n,1)) bitch=0;
					else
					{
						bitch=1;
						if(u>1)
						bitch+=(u-xquery(1,u-1,1,n,1)-1);
						if(u<n)
						bitch+=(yquery(u+1,n,1,n,1)-u-1);
					}
					printf("%d\\n",bitch);
				}
			}
		 } 
	}
	return 0;
}

 J-light oj 1120

扫描线问题需要会离散化,区间更新。而且这里的区间更新非常灵活,不是pushdown,而是pushup。根据需要解锁不同姿势。。。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll __int64
const int maxn=30008;
struct fuck{
	int x,y,u,v;
	int idx;
	int w;
	bool operator<(const fuck &a)	const
	{
		return idx<a.idx;
	}
}f[maxn<<1];
int tol;
int re;
int a[maxn<<1];
int b[maxn<<1];
ll st[maxn<<4];
int lazy[maxn<<4];
int shit[maxn];
int bs(int x)
{
	int left,right;
	left=1;right=re;
	while(left<=right)
	{
		int mid=(left+right)>>1;
		if(b[mid]<x)	left=mid+1;
		else	right=mid-1;
	}
	return left;
}
void init()
{
	memset(st,0,sizeof(st));
	memset(lazy,0,sizeof(lazy));
}
void pushup(int c,int x,int y)
{
	if(lazy[c])	st[c]=b[y+1]-b[x];
	else if(x==y)	st[c]=0;
	else	st[c]=st[c<<1]+st[c<<1|1];
}
void update(int sx,int sy,int w,int x,int y,int c)
{
	if(sx>sy)	return;
	if(sx<=x&&y<=sy)
	{
		lazy[c]+=w;
		pushup(c,x,y);
		return; 
	}
	int mid=(x+y)>>1;
	if(sx<=mid)	update(sx,sy,w,x,mid,c<<1);
	if(sy>mid)	update(sx,sy,w,mid+1,y,c<<1|1);
	pushup(c,x,y);
	return;
}
int main()
{
	int i,j,t,n,m,x,y,sx,sy,u,v;
	int cas=1;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		tol=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d%d%d%d",&x,&y,&sx,&sy);
			f[++tol].idx=x;f[tol].x=y;f[tol].y=sy;
			a[tol]=y;f[tol].w=1;
			f[++tol].idx=sx;f[tol].x=y;f[tol].y=sy;
			a[tol]=sy;f[tol].w=-1;
		}
		sort(f+1,f+tol+1);
		sort(a+1,a+tol+1);
	//	printf("%d\\n",tol);
		a[0]=-1;re=0;
		for(i=1;i<=tol;i++)
			if(a[i]!=a[i-1])
				b[++re]=a[i];
		ll sum=0;
		init();
		for(i=1;i<=tol;i++)
		{
			u=bs(f[i].x);
			v=bs(f[i].y);
			//printf("%d %d\\n",u,v);
			if(i==1)
			update(u,v-1,f[i].w,1,re,1);
			else
			{
			//	printf("%d\\n",st[1]);
				sum+=st[1]*((ll)f[i].idx-(ll)f[i-1].idx);
				update(u,v-1,f[i].w,1,re,1);
			}
		}
		printf("Case %d: %lld\\n",cas++,sum);
	}
	return 0;
}

 L-uvalive 4730 kingdom

大白薯上的题。抓这题是liji的主意。线段树+并查集。

在二维坐标上有一系列的点,每个点代表一个城市。进行一系列的操作。一种操作是将两个城市相连接,互通的城市形成一个州。另一个操作是询问y直线一共穿过了多少州和城市

用并查集维护州的相连情况,然后分类讨论,更新y轴的城市和州分布情况,又是区间更新加离散化orz。

分类讨论其实很简单。你需要仔细想想,什么情况下点y的州数目和城市数目变化,什么情况不变化,按照思路更新即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define db double
const int maxn=200008;
struct poi{
	int x,y;
	int idx;
	bool operator<(const poi &a)	const
	{
		return y<a.y;
	}
}f[maxn];
int g[maxn];
int rel[maxn];
int a[maxn<<4],b[maxn<<4];
int lazya[maxn<<4],lazyb[maxn<<4];
struct fuck{
	int next;
	int x,y;
	int w;
}uni[maxn];
void init(int n)
{
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(lazya,0,sizeof(lazya));
	memset(lazyb,0,sizeof(lazyb));
	for(int i=1;i<=n;i++)
	{
		uni[i].next=i;
		uni[i].x=uni[i].y=rel[i];
		uni[i].w=1;
	}
}
int find(int u)
{
	if(u==uni[u].next)	return u;
	return uni[u].next=find(uni[u].next);
}
void pushdowna(int c)
{
	if(lazya[c]==0)	return;
	lazya[c<<1]+=lazya[c];
	lazya[c<<1|1]+=lazya[c];
	a[c<<1]+=lazya[c];
	a[c<<1|1]+=lazya[c];
	lazya[c]=0;
}
void pushdownb(int c)
{
	if(lazyb[c]==0)	return;
	lazyb[c<<1]+=lazyb[c];
	lazyb[c<<1|1]+=lazyb[c];
	b[c<<1]+=lazyb[c];
	b[c<<1|1]+=lazyb[c];
	lazyb[c]=0;
}
void update(int sx,int sy,int w,int x,int y,int c)
{
	if(sx>sy)	return;
	if(sx<=x&&y<=sy)
	{
		a[c]+=w;
		lazya[c]+=w;
		return;
	}
	pushdowna(c);
	int mid=(x+y)>>1;
	if(sx<=mid)	update(sx,sy,w,x,mid,c<<1);
	if(mid<sy)	update(sx,sy,w,mid+1,y,c<<1|1);
	return;
}
void updateb(int sx,int sy,int w,int x,int y,int c)
{
	if(sx>sy)	return;
	if(sx<=x&&y<=sy)
	{
		b[c]+=w;
		lazyb[c]+=w;
		return;
	}
	pushdownb(c);
	int mid=(x+y)>>1;
	if(sx<=mid)	updateb(sx,sy,w,x,mid,c<<1);
	if(mid<sy)	updateb(sx,sy,w,mid+1,y,c<<1|1);
	return;
}
int bs(db shit,int n)
{
	int left,right;
	left=1;right=n;
	while(left<=right)
	{
		int mid=(left+right)>>1;
		if(f[mid].y<shit)	left=mid+1;
		else	right=mid-1; 
	}
	return left-1;
}
int query(int sx,int x,int y,int c)
{
	if(x==y)
	{
		return a[c];
	}
	pushdowna(c);
	int mid=(x+y)>>1;
	if(sx<=mid)	return query(sx,x,mid,c<<1);
	else	return query(sx,mid+1,y,c<<1|1);
}
int queryb(int sx,int x,int y,int c)
{
	if(x==y)
	{
		return b[c];
	}
	pushdownb(c);
	int mid=(x+y)>>1;
	if(sx<=mid)	return queryb(sx,x,mid,c<<1);
	else	return queryb(sx,mid+1,y,c<<1|1);
}
int main()
{
	int i,j,t,n,m,u,v,x,y;
	scanf("%d",&t);
	char s[10];
	while(t--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d%d",&f[i].x,&f[i].y);
			f[i].idx=i;
		}
		sort(f+1,f+1+n);
		for(i=1;i<=n;i++)
			g[f[i].idx]=i; 
		int pre=-1,k=0;
		for(i=1;i<=n;i++)
		{
			if(f[i].y!=pre)
				rel[i]=++k;
			else
				rel[i]=k;
			pre=f[i].y;
		} 
		init(n);
		scanf("%d",&m);
		while(m--)
		{
			scanf("%s",s);
			if(s[0]==\'r\')
			{
				scanf("%d%d",&u,&v);
				u++;v++;
				u=g[u];v=g[v];
				x=find(u);y=find(v); 
				if(x!=y)
				{
					if(uni[x].y>uni[y].y){
						int shit=x;x=y;y=shit;
					}
					if(uni[x].y<uni[y].x)
					{
						update(uni[x].y,uni[y].x-1,1,1,k,1);
						updateb(uni[x].y,uni[y].x-1,uni[y].w+uni[x].w
								,1,k,1);
						updateb(uni[x].x,uni[x].y-1,uni[y].w,1,k,1);
						updateb(uni[y].x,uni[y].y-1,uni[x].w,1,k,1);
					}
					else
					{
						update(max(uni[y].x,uni[x].x),
								uni[x].y-1,-1,1,k,1);
						updateb(uni[x].x,uni[y].x-1,uni[y].w,1,k,1);
						updateb(uni[y].x,uni[x].x-1,uni[x].w,1,k,1);
						updateb(uni[x].y,uni[y].y-1,uni[x].w,1,k,1);
					}
					uni[x].next=y;
					uni[y].w+=uni[x].w;
					uni[y].x=min(uni[x].x,uni[y].x);
				}
			}
			else
			{
				db shit;
				scanf("%lf",&shit);
				if(shit<f[1].y)
				{
					printf("0 0\\n");
					continue;
				}
				x=bs(shit,n);
				x=rel[x];
				int sx=query(x,1,k,1);
				int sy=queryb(x,1,k,1);
				printf("%d %d\\n",sx,sy);
			 } 
		}
	}
	return 0;
}

 

 

以上是关于HUT 线段树练习 部分题解的主要内容,如果未能解决你的问题,请参考以下文章

CODEVS 1081 线段树练习 2 题解

CodeVS 4927-线段树练习5

线段树练习3

FJUT3574 HOME_W的附加题(带权线段树)题解

线段树练习 codevs 1080

HDU 1556 Color the Ball 线段树 题解