防爆秃击队——线段树/树状数组/cdq分治专题训练(二)

//博主过菜,基本本篇的思路全为网上来源!!!

K - Glass Carving(CF.527C)

1.题目描述:
Leonid wants to become a glass carver (the person who creates beautiful artworks by cutting the glass). He already has a rectangular w mm  ×  h mm sheet of glass, a diamond glass cutter and lots of enthusiasm. What he lacks is understanding of what to carve and how.

In order not to waste time, he decided to practice the technique of carving. To do this, he makes vertical and horizontal cuts through the entire sheet. This process results in making smaller rectangular fragments of glass. Leonid does not move the newly made glass fragments. In particular, a cut divides each fragment of glass that it goes through into smaller fragments.

After each cut Leonid tries to determine what area the largest of the currently available glass fragments has. Since there appear more and more fragments, this question takes him more and more time and distracts him from the fascinating process.

Leonid offers to divide the labor — he will cut glass, and you will calculate the area of the maximum fragment after each cut. Do you agree?

Input
The first line contains three integers w, h, n (2 ≤ w, h ≤ 200 000, 1 ≤ n ≤ 200 000).

Next n lines contain the descriptions of the cuts. Each description has the form H y or V x. In the first case Leonid makes the horizontal cut at the distance y millimeters (1 ≤ y ≤ h - 1) from the lower edge of the original sheet of glass. In the second case Leonid makes a vertical cut at distance x (1 ≤ x ≤ w - 1) millimeters from the left edge of the original sheet of glass. It is guaranteed that Leonid won’t make two identical cuts.

Output
After each cut print on a single line the area of the maximum available glass fragment in mm2.

Examples
Input
4 3 4
H 2
V 2
V 3
V 1
Output
8
4
4
2
Input
7 6 5
H 4
V 3
V 5
H 2
V 1
Output
28
16
12
6
4
Picture for the first sample test:
在这里插入图片描述

Picture for the second sample test:
在这里插入图片描述
2.题意:
给一块h*w的玻璃,然后每次水平或竖直地切割玻璃,每次切完输出当前最大的玻璃面积。

3.思路:
1)暴力set模拟。
由于长与宽彼此独立,所以我们可以直接模拟地去做,先用两个set记录下被割的点,用另外的两个multiset记录下最大的长度(由于长度可能会重复,所以要用multiset)。由于分割的点不会重复,所以我们先加入0,w这两个点,然后每一次把割点加入set里,然后找出割点左右的两个点,把这两个点的长度删除,加入他们分别于割点的距离,然后每次都输出最大的长与宽相乘就好了。
2)线段树(待补)。

4.代码:
(1)暴力:

//K - Glass Carving
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<set>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?a:-a)
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<n;++i)
#define per(i,n,a) for(int i=n-1;i>=a;--i)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long ll;
const int maxn = 200000+5;
const int maxm = 100000+5;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll read()
{
	ll x=0,sign=1;
	char c=getchar();
	while(c>'9'||c<'0') {if(c=='-') sign=-1;c=getchar();}
	while('0'<=c&&c<='9') x=x*10+c-'0',c=getchar();
	return x*sign;
}
//head
ll w,h,n,x;
set<ll> H,V;
multiset<ll> lenH,lenV;
inline void solve()
{
	H.clear(),V.clear();
	lenH.clear(),lenV.clear();
	H.insert(0),H.insert(h);
	V.insert(0),V.insert(w);
	lenH.insert(h),lenV.insert(w);
	char c=getchar();
	while(n--)
	{
		c=getchar();
		x=read();
		if(c=='V')
		{
			V.insert(x);
			ll begin=*(--V.find(x));
			ll end=*(++V.find(x));
			lenV.erase(lenV.find(end-begin));
			lenV.insert(x-begin);
			lenV.insert(end-x);
		}
		else
		{
			H.insert(x);
			ll begin=*(--H.find(x));
			ll end=*(++H.find(x));
			lenH.erase(lenH.find(end-begin));
			lenH.insert(x-begin);
			lenH.insert(end-x);
		}
		ll maxv=*lenV.rbegin(),maxh=*lenH.rbegin();
		printf("%lld\n",maxv*maxh);
	}
	//puts("");
}
int main()
{
	//freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
	while(~scanf("%lld%lld%lld",&w,&h,&n)) solve();
	return 0;
}

L - Inna and Sequence(CF. 374D)

1.题目描述:
Dima’s spent much time thinking what present to give to Inna and gave her an empty sequence w. Now they want to fill sequence w with numbers zero and one. For that, they decided to play an amusing game.

Before the game begins, Dima chooses m integers a1, a2, …, am (1 ≤ a1 < a2 < … < am). Then Inna and Dima start playing, that is, adding numbers to sequence w. Each new number they choose is added to the end of the sequence. At some moments of time Dima feels that the game is going to end too soon (and he wants to play with Inna as long as possible), so he hits a table hard with his fist. At that the a1-th, a2-th, a3-th, …, ak-th numbers from the beginning simultaneously fall out of the sequence (the sequence gets k numbers less). Here k is such maximum number that value ak doesn’t exceed the current length of the sequence. If number a1 is larger than the current length of w, then nothing falls out of the sequence.

You are given the chronological sequence of events in the game. Each event is either adding a number to the end of sequence w or Dima’s hit on the table. Calculate the sequence w after all these events happen.

Input
The first line of the input contains two integers n and m (1 ≤ n, m ≤ 106) showing how many events took place and how many numbers Dima chose.

The next line contains m distinct integers ai (1 ≤ ai ≤ 106) sorted in the increasing order.

Next n lines describe the events in the chronological order. Each line contains a single integer: -1, 0 or 1. Number -1 means that Dima hits the table. Number 0 means that Inna and Dima add number 0 to the end of the sequence. Number 1 means that Inna and Dima add number 1 to the end of the sequence.

Output
In a single line print a sequence of numbers 0 and 1 — the elements of the sequence after all events happen. Print the elements of the sequence in the order from the beginning to the end of the sequence.

If after all events the sequence ends up empty, print “Poor stack!”.

Examples
Input
10 3
1 3 6
-1
1
1
0
0
-1
0
1
-1
1
Output
011
Input
2 1
1
1
-1
Output
Poor stack!

2.题意:
有两个人填序列(只填0、1),一个人觉得不过瘾,就会锤桌子(-1),然后填好的数字就会按某种顺序掉出来。问最后序列的样子。

3.思路:
这题其实涉及到了动态地查询,删除和增加,本人想了很久莫得思路。参考网上的博客,发现思路原来是:二分+树状数组。时间复杂度是:利用树状数组的区间求和达到O(logn)的查询,然后删除和增加操作则为单点修改的O(logn)。实现方法是:利用权值树状数组的前缀和作为动态的下标,然后二分的在这个动态下标下寻找我要删除的位置的元素,要先记录,最后一起删除。由于权值树状数组的前缀和会多次重复(因为会有被删除的元素的位置空出来),那么前缀和突变位置就是我元素没有被删除的位置增加和删除都是对树状数组对应绝对下标下的加一与减一操作

4.代码:

//L - Inna and Sequence
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define FAST ios::sync_with_stdio(false)
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) (x<0?-x:x)
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,n) for(int i=a;i<n;++i)
#define per(i,a,n) for(int i=n-1,i>=a;--i)
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
const int inf=0x7fffffff;
const double eps = 1e-7;
inline int lowbit(int x){return x&(-x);}
inline int read()
{
	int x=0,sign=1;
	char c=getchar();
	while(c>'9'||c<'0') {if(c=='-') sign=-1;c=getchar();}
	while('0'<=c&&c<='9') x=x*10+c-'0',c=getchar();
	return x*sign;
}
//head
int n,m,x;
int c[maxn],a[maxn],num[maxn],rm[maxn];
inline void add(int x,int k)
{
	while(x<=n)
	{
		c[x]+=k;
		x+=lowbit(x);
	}
}
inline int sum(int x)
{
	int s=0;
	while(x)
	{
		s+=c[x];
		x-=lowbit(x);
	}
	return s;
}
inline void solve()
{
	mem(c,0);
	mem(rm,0);
	rep(i,0,m) a[i]=read();
	int w=0;
	rep(i,0,n)
	{
		x=read();
		if(x<0)
		{
			int cnt=0;
			rep(j,0,m)
			{
				int l=0,r=w;
				if(sum(w)<a[j]) break;
				while(l<r)
				{
					int mid=(l+r)>>1;
					if(sum(mid)>=a[j]) r=mid;
					else l=mid+1;
				}
				rm[cnt++]=r;
			}
			rep(j,0,cnt) add(rm[j],-1);
		}
		else
			num[++w]=x,add(w,1);
	}
	bool flag=0;
	rep(i,1,w+1)
		if(sum(i)-sum(i-1)==1)
		{
			flag=1;
			printf("%d",num[i]);
		}
	if(!flag) printf("Poor stack!");
	printf("\n");
}
int main()
{
	//freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
	while(~scanf("%d%d",&n,&m)) solve();
	return 0;
}

补充题

1.题目描述:
在这里插入图片描述
2.题意:
给定一个数组a,用最少的变化,把这个数组变成a’,并且a’的树状数组b与a’数组相同。输出修改后的数组a’。【题目的b就是我们惯用的c数组,下面用c代替题目中的b】

3.思路:
因为这个题是个对树状数组理解的好题。所以博主特地加到了这里。我们考虑树状数组c的定义是c[i]:=( i-lowbit(i),i ] 。除了这个定义以为,还有一个直观的定义,就是子节点与自身对应下标的原数组的值之和,也即:c[i]=a[i]+c[i]的子节点的值之和。因此,我们要让c[i]=a[i],那必然让c[i]的子节点的值之和为0。到这一步如果觉得懵的话就先去再理解一下树状数组哦!有了这个,我们就满足了第一个条件,那么怎么确保我们的修改最少呢?我们想,如果让某一个数变成其他数之和的相反数,那么我们的修改是不是就最小啦!但是我们前面修改过的c数组的值已经确定了,后面如果再改就会破坏前面已经确定的值了!这个提示我们:要找树状数组的叶子节点!因为它能做的影响很少,没有儿子的牵挂,就不怕修改掉前面的值啦!那么树状数组的叶子节点是啥呢?如果你熟悉树状数组的话,会发现就是所有的奇数节点,那么我们就选择它来修改就好啦!那么我们最终得到我们要修改的值啦!因此我们最后总结一下:我们要修改的节点是c[i]!=a[i]的奇数子节点,把奇数子节点的值变为其余子节点的值之和的相反数;如果没有其余子节点,就置为0。【提醒细节:对于不一定最后能够凑成一颗完整的树,所以我们最后的n要分情况讨论一下:如果n是偶数,那我前面的操作保证了我能够刚好取得a[n]的值;如果n是奇数,如果我不单独拎出来,最后可能会被修改掉!而我奇数本身应该是a[n]的,所以我们最后单独输入c[n]的值】。

4.代码:

//
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define FAST ios::sync_with_stdio(false)
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<n;++i)
#define per(i,a,n) for(int i=n-1,i>=a;--i)
using namespace std;
typedef long long ll;
const int maxn = 200000+5;
const int inf=0x7fffffff;
const double eps = 1e-7;
inline ll lowbit(int x){return x&(-x);}
inline ll read()
{
	ll x=0,sign=1;
	char c=getchar();
	while(c>'9'||c<'0') {if(c=='-') sign=-1;c=getchar();}
	while('0'<=c&&c<='9') x=x*10+c-'0',c=getchar();
	return x*sign;
}
//head
ll n,m,x;
ll c[maxn];
inline ll get(ll i)
{
	ll res=0;
	for(int j=i;j>(i+1)-lowbit(i+1);j-=lowbit(j))
		res+=c[j];
	return -res;
}
inline void solve()
{
	rep(i,1,n)
	{
		x=read();
		if(i&1) c[i]=get(i);
		else c[i]=x;
	}
	c[n]=read();
	rep(i,1,n+1) printf("%lld ",c[i]);
	cout<<"\n";
}
int main()
{
	freopen("fenwick.in","r",stdin);
	freopen("fenwick.out","w",stdout);
	while(~scanf("%lld",&n)) solve();
	return 0;
}

C - Apple Tree (POJ - 3321)

1.题目描述:
There is an apple tree outside of kaka’s house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been carefully nurturing the big apple tree.

The tree has N forks which are connected by branches. Kaka numbers the forks by 1 to N and the root is always numbered by 1. Apples will grow on the forks and two apple won’t grow on the same fork. kaka wants to know how many apples are there in a sub-tree, for his study of the produce ability of the apple tree.

The trouble is that a new apple may grow on an empty fork some time and kaka may pick an apple from the tree for his dessert. Can you help kaka?
在这里插入图片描述

Input
The first line contains an integer N (N ≤ 100,000) , which is the number of the forks in the tree.
The following N - 1 lines each contain two integers u and v, which means fork u and fork v are connected by a branch.
The next line contains an integer M (M ≤ 100,000).
The following M lines each contain a message which is either
“C x” which means the existence of the apple on fork x has been changed. i.e. if there is an apple on the fork, then Kaka pick it; otherwise a new apple has grown on the empty fork.
or
“Q x” which means an inquiry for the number of apples in the sub-tree above the fork x, including the apple (if exists) on the fork x
Note the tree is full of apples at the beginning

Output
For every inquiry, output the correspond answer per line.
Sample Input
3
1 2
1 3
3
Q 1
C 2
Q 1
Sample Output
3
2

2.题意:
卡卡家里有几颗苹果树,他会给树标上序号,根节点总为1;他会随机访问一些节点,如果有苹果就摘下苹果;如果没有苹果,苹果就会长出来。(0->1;1->0)他还会随机查询一颗子树的苹果数。

3.思路:
树状数组+DFS序。对于这种树上问题,我们有个很好的化树为区间的方法,那就是DFS序,我们对一颗树遍历时,采取DFS顺序记下进入该节点的时间(从子树退回时不用记录),再记录离开节点的时间(遍历完所有子树后再次回溯到自身的那个时间),那么这一段时间我们把它用离散的值1,2,3这样表示就会发现可以形成一个以进出时间为端点的区间。我们有了这个区间之后,就可以在上面进行操作了。我们先处理出这个连续的时间轴,因为节点和它进入的时间轴一一对应,用权值树状数组维护它的前缀和。查询的时候用离开的时间减去进入的时间减一(看成是左右端点就OK了),就是子树的苹果数了。修改则直接开一个布尔数组用来表示原本的树,判断有无苹果,有则对应的进入时间点减一,否则加一;别忘了更新布尔数组。

4.代码:

//C - Apple Tree POJ - 3321 
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define FAST ios::sync_with_stdio(false)
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<n;++i)
#define per(i,a,n) for(int i=n-1,i>=a;--i)
using namespace std;
typedef long long ll;
const int maxn = 100000+5;
const int maxm = 100000+5;
const int inf=0x7fffffff;
const double eps = 1e-7;
inline int lowbit(int x){return x&(-x);}
inline int read()
{
	int x=0,sign=1;
	char c=getchar();
	while(c>'9'||c<'0') {if(c=='-') sign=-1;c=getchar();}
	while('0'<=c&&c<='9') x=x*10+c-'0',c=getchar();
	return x*sign;
}
//head
int n,m,x;
char ch;
int time,cnt,u,v;
int in[maxn],out[maxn],c[maxn];
int head[maxn];
bool tree[maxn];
void init()
{
	time=0;
	cnt=0;
	mem(in,0);
	mem(out,0);
	mem(head,0);
}
struct Edge
{
	int v,nx;
}edge[maxm<<1];
inline void add(int u,int v)
{
	edge[++cnt]=(Edge){v,head[u]};
	head[u]=cnt;
}
//
void dfsn(int u,int fa)
{
	in[u]=++time;
	for(int i=head[u];i;i=edge[i].nx)
	{
		int v=edge[i].v;
		if(v==fa) continue;
		dfsn(v,u);
	}
	out[u]=time;
}
//
inline void change(int x,int k)
{
	while(x<=n)
	{
		c[x]+=k;
		x+=lowbit(x);
	}
}
inline int query(int x)
{
	int s=0;
	while(x)
	{
		s+=c[x];
		x-=lowbit(x);
	}
	return s;
}
//
inline void solve()
{
	init();
	rep(i,1,n)
	{
		u=read(),v=read();
		add(u,v),add(v,u);//单向边也可以 
	}
	dfsn(1,-1);
	//dfsn(1,1);//父节点为自身也可以 
	m=read();
	rep(i,1,n+1)
	{
		tree[i]=1;
		change(i,1);
	}
	while(m--)
	{
		ch=getchar(),x=read();
		if(ch=='C')
		{
			change(in[x],tree[x]?-1:1);
			tree[x]^=1;
		}
		else
			printf("%d\n",query(out[x])-query(in[x]-1));
	}
}
int main()
{
	//freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
	while(~scanf("%d",&n)) solve();
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值