HUT 线段树练习 部分题解

F-poj 2886

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

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

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

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

可以打反素数表的方式来做(百度即可),但这里介绍另一种方法,就是我们可以直接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;
}
   
时间: 2024-10-12 14:46:02

HUT 线段树练习 部分题解的相关文章

CODEVS 1081 线段树练习 2 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://codevs.cn/problem/1081/ 题目描述 Description 给你N个数,有两种操作 1:给区间[a,b]的所有数都增加X 2:询问第i个数是什么? 输入描述 Input Description 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数.如果第一个数是1,后接3个正整数a,b,X,表示在区间[a,b]内每个数增加X,如果

Codeforces 750E New Year and Old Subsequence 线段树 + dp (看题解)

New Year and Old Subsequence 第一感觉是离线之后分治求dp, 但是感觉如果要把左边的dp值和右边的dp值合起来, 感觉很麻烦而且时间复杂度不怎么对.. 然后就gun取看题解了, 用线段树维护dp的值, 然后区间合并求答案. 每个节点保存dp[ i ][ j ]表示, 把当前管理的区间删到 s{2017}中的 s[ i + 1 ] - s[ j - 1 ],最少删几个, 然后合并的时候5 ^ 3合并. #include<bits/stdc++.h> #define L

Codeforces 283E Cow Tennis Tournament 线段树 (看题解)

Cow Tennis Tournament 感觉这题的难点在于想到求违反条件的三元组.. 为什么在自己想的时候没有想到求反面呢!!!! 违反的三元组肯定存在一个人能打败其他两个人, 扫描的过程中用线段树维护一下就好了. 反思: 计数问题: 正难则反 正难则反 正难则反 !!!! #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #define fi

Codeforces 1076G Array Game 博弈 + 线段树 (看题解)

Array Game 考虑最裸的dp去求胜负态. dp[ i ] 从后面的 m 个状态转移过来. 我们考虑如何用线段树维护, T[ i ][ mask ] 表示 i 这段区间如果后面接的m位是mask使时开头m位的mask, 对于修改的话只要维护一个反过来的T2就可以了. 感觉是可以想出来的题, 为什么没想出来啊啊啊. #include<bits/stdc++.h> #define LL long long using namespace std; const int N = (int)2e5

HDU - 5770 Treasure 思维 + 扫描线 + 线段树 (看题解)

HDU - 5770 没想出来, 感觉不应该啊, 没有想到转换成二维上的点的问题. 对于对钥匙和宝藏(u, v), 如果lca != u && lca != v 那么起点从u子树出发, 终点在v子树就能得到贡献. 子树在dfs序下是连续一段, 所以就对应到二维平面一个矩形加上一个数值, 求值最大的点. 对于lca == u || lca == v同样可以讨论出来. 还有一种情况就是u == v, 我们先把贡献都加上, 然后对于不经过u 的所有路径进去这个贡献. 然后扫描线扫一遍就好了. #

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

POJ 2528 Mayor&#39;s posters 离散化和线段树题解

本题就是要往墙上贴海报,问最后有多少可见的海报. 其实本题的难点并不是线段树,而是离散化. 因为数据很大,直接按原始数据计算那么就会爆内存和时间的. 故此需要把数据离散化. 比如有海报1 6   7 9   20 100  5 1000的原始数据,直接计算需要1-1000的内存,离散化之后只需要8内存,因为只有4组数据8个数. 本题更进一步高级一点的离散化就是需要把不相邻的两个数据插入一个数值,表示有空白的地方,不是所有海报都覆盖到的. 比如上面的数据要离散为:1 2  5 6  7 8 9 1

POJ 2528 Mayor&amp;#39;s posters 离散化和线段树题解

本题就是要往墙上贴海报,问最后有多少可见的海报. 事实上本题的难点并非线段树,而是离散化. 由于数据非常大,直接按原始数据计算那么就会爆内存和时间的. 故此须要把数据离散化. 比方有海报1 6   7 9   20 100  5 1000的原始数据.直接计算须要1-1000的内存,离散化之后仅仅须要8内存,由于仅仅有4组数据8个数. 本题更进一步高级一点的离散化就是须要把不相邻的两个数据插入一个数值.表示有空白的地方,不是全部海报都覆盖到的. 比方上面的数据要离散为:1 2  5 6  7 8

BZOJ1798题解 Seq维护序列题解 双tag裸线段树

BZOJ1798题解 Seq维护序列题解 双tag裸线段树 1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 5549  Solved: 1951[Submit][Status][Discuss] Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)