HDU-6406-Taotao Picks Apples(四种方法,完成2)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6406

题目大意:给出一个序列,让你从头遍历到末尾,第一个数必须选,后面的,没碰到一个数,如果这个数大于前面选择的那个数,就再次选择这个数,给出m次操作,将x位置的数修改为val,并查询修改后能选择多少个数?(每次操作是独立的,即对序列不产生影响)。

思路:比赛时没有想出来,只想着用前缀和什么的,似乎用二分,但是由于无序也不知道怎么二分。之后看题解直到怎么二分了,解锁二分新姿势。

有多种解题方法,看着都不难。(标程一个不会,整天就搞些骚东西QAQ....)

目录

1.标称为ST表+二分

2.延伸的线段树+二分(既然ST表都能用,那线段树肯定也能用啦)

3.整体二分+局部二分(实现细节比较多)(已实现)

4. dalao 的纯线段树做法(膜)


1.标称为ST表+二分

思路:未实现

2.延伸的线段树+二分(既然ST表都能用,那线段树肯定也能用啦)

思路:未实现

3.整体二分+局部二分(实现细节比较多)(已实现)

思路:首先我们根据初试序列得到一个答案序列B(即完全递增序列),然后得到B中元素多对应原数组A中的位置数组Pos。然后根据Pos,对每个小区间在进行依次获得小答案序列Vec[Pos],便于后面进行局部二分。

然后我们每次修改的时候会有以下几种情况:

1.修改处于B数组中的元素:有一下几种情况:

            a.将该元素变大,那么会对后面的递增序列产生影响(可能会挤掉一些元素)因此我们直接减去影响的区间即可。

            b.将该元素变小,那么会对后面的递增序列产生影响(可能会增加一些元素),因此我们二分之前得到的局部区间即可找到增加的元素的个数。当然,可能变得比前面的有效元素还要小,那么我们比较一下,选更大的那个即可(因为只影响B,A中的无效元素可以说都小于max(val,B[qPos-1]))。

2.修改不处于B数组中的元素:有以下几种情况:

           a.将该元素变大同1a。注意由于此时修改的不是B中的元素,因此qPos位于x之前。

           b.将该元素变小,本来就没有影响,因为都变小了,所以更没有影响啦!。

说的可能不是很清楚,给组样例说明一下吧:这组样例,我们来修改一下:

4 5:

4 7:掠过6,Ans=4-(4-1-(2+1))=4

4 8:掠过6,8:Ans=(4-(5-1-(2+1))=3

3 1:变小了:修改后面的数组:resB: 2 3 4 6 8,减少4新增3 4,Ans=5.

3 5:同理

.......

由于细节较多,我就在代码中进行注释了:

ACCode:400+ms,还挺快

//#pragma comment(linker, "/STACK:1024000000,1024000000")
  
#include<stdio.h>
#include<string.h> 
#include<math.h> 
   
#include<map>  
#include<set>
#include<deque> 
#include<queue> 
#include<stack> 
#include<bitset>
#include<string> 
#include<fstream>
#include<iostream> 
#include<algorithm> 
using namespace std; 
  
#define ll long long 
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// ??
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;

int A[MAXN];
int B[MAXN],Pos[MAXN],tot;
vector<int> Vec[MAXN];
int n,m;

int FindSeq(int Key){
	int l=1,r=tot,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(Pos[mid]==Key) return 1;
		else if(Pos[mid]<Key) l=mid+1;
		else r=mid-1;
	}return 0;
}
int GetBR(int Key){
	int l=1,r=tot,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(B[mid]>Key) r=mid-1;
		else l=mid+1;
	}return l;
}
int GetPos(int Key){
	int l=1,r=tot,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(Pos[mid]==Key) return mid;
		else if(Pos[mid]>Key) r=mid-1;
		else l=mid+1;
	}return r;
}
int GetVec(int qPos,int Key){
	int l=0,r=Vec[qPos].size()-1,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(Vec[qPos][mid]>Key) r=mid-1;
		else l=mid+1;
	}return l;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i){
    		scanf("%d",&A[i]);
		}
		tot=0;
		for(int i=1;i<=n;++i){
			if(i==1||A[i]>B[tot]){
				B[++tot]=A[i];
				Pos[tot]=i;
			}
		}B[++tot]=INF32;Pos[tot]=n+1;A[n+1]=INF32;
		for(int i=1;i<=n;++i) Vec[i].clear();
		for(int i=1;i<tot;++i){
			if(Pos[i]+1<Pos[i+1]){
				Vec[i].push_back(A[Pos[i]+1]);
				for(int j=Pos[i]+2;j<Pos[i+1];++j){
					if(A[j]>Vec[i][Vec[i].size()-1]){
						Vec[i].push_back(A[j]);
					}
				}
			}
		}
//		printf("B[]: ");for(int i=1;i<=tot;++i) printf("%d ",B[i]);printf("\n");
//		printf("Pos: ");for(int i=1;i<=tot;++i) printf("%d ",Pos[i]);printf("\n");
		for(int i=1;i<=m;++i){
			int x,val;scanf("%d%d",&x,&val);
			if(FindSeq(x)){//在递增序列中 
				if(A[x]<val){//变大 对后面产生影响 可以将后面的剔除出去 
					int qPos=GetPos(x);//获得当前位置
					int qBR=GetBR(val);//获得后面 > val的位置 
//					printf("在递增序列中&&变大:tot=%d qPos=%d qBR=%d\n",tot,qPos,qBR);
					//此时,[qPos,qBR-1]中的所有元素都变成了val 
					printf("%d\n",tot-1-(qBR-1-qPos));
					continue;
				}
				//变小||不变,当前位置作废,相当于重新在[Pos-1,Pos+1]之间插入一个val 
				int qPos=GetPos(x);//获得当前位置 
				if(val>B[qPos-1]){//仍然大于前面的那个元素 
					int qBR=GetVec(qPos,val);qBR=Vec[qPos].size()-qBR;
					//此时,[qBR,Vec[qPos].size()]中都是新加入的 .
//					printf("在递增序列中&&变小,但还大:tot=%d Add=%d\n",tot,qBR+1);
					printf("%d\n",tot-1+qBR);
					continue;
				}
				//小于等于前面那个元素 
				int qBR=GetVec(qPos,B[qPos-1]);qBR=Vec[qPos].size()-qBR;
//				printf("在递增序列中&&小于前面元素: tot=%d Add=%d\n",tot,qBR);
				printf("%d\n",tot-1+qBR-1);
				continue;
			}
			//不在递增序列中 
			if(A[x]<val){//变大,可能对后面产生影响,可以将后面的顶掉 
				int qPos=GetPos(x);//获得前面的位置 x位于[Pos~Pos+1]区间内 
				int qBR=GetBR(val);//获得后面的 > val 的元素位置 
				if(val>B[qPos]){//大于前面的那个数,新增加一个 
					//此时,[qPos+1,qBR-1]中的所有元素都变成了val 
//					printf("不再递增序列中&&更大:tot=%d qPos=%d qBR=%d\n",tot,qPos,qBR);
					printf("%d\n",tot-1-(qBR-1-qPos-1));
					continue;
				}
				//小于前面的没影响 
//				printf("不在递增序列中&&比前面的小,无影响\n");
				printf("%d\n",tot-1);
				continue;
			}
			//变小||不变,没有影响 
//			printf("不在递增序列中&&变小,无影响\n");
			printf("%d\n",tot-1);
		}
	}
}

/*

2
10 5
8 3 1 3 12 3 4 6 9 2
1 2
2 3
3 4
4 5
7 8

9 10
2 1 4 3 4 6 3 8 3
4 5
4 7
2 5
2 7
2 8
2 1
3 5
3 7
3 8
3 1

*/

4. dalao 的纯线段树做法(膜)

正解一个不会,满脑子骚方法,我凉了QAQ。。。

找ST表+二分的题解的时候,找到了个这个,看着思路似乎很简单,就试着写了些,T了几发,总算找到精髓了。膜神仙。

大佬:这不是BZOJ原题嘛!,大佬链接:https://blog.csdn.net/weixin_39453270/article/details/81784154

思路:建一棵树,每个节点维护区间最值和区间有效长度。然后就是对其更新和查询,

左节点最大值<=val只访问右节点。//意思是左节点维护的区间没有用,我们要重新对右节点进行维护,所以仍要访问。

否则 ans=右子树的有效长度(之前处理过了)+向左子树查找(log n)//由于右子树是根据左节点得出的,因此左子树有效的话,右子树就是之前处理的值,直接相加即可。然后只用维护左子树。

ACCode:900+ms,虽然慢,但是码量少,也好写,熟练的话10min+就出来了。果然当时看榜单的时候那个10min的可能就是这个方法了(毕竟BZOJ原题QAQ)

//#pragma comment(linker, "/STACK:1024000000,1024000000")
  
#include<stdio.h>
#include<string.h> 
#include<math.h> 
   
#include<map>  
#include<set>
#include<deque> 
#include<queue> 
#include<stack> 
#include<bitset>
#include<string> 
#include<fstream>
#include<iostream> 
#include<algorithm> 
using namespace std; 
  
#define ll long long 
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// ??
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;

class Segment{
	int Tree[MAXN<<2],Cnt[MAXN<<2];
	
	public : void Build(int l,int r,int rt,int A[]){
		if(l==r){
			Tree[rt]=A[l];Cnt[rt]=1;
			return ;
		}
		int mid=(l+r)>>1;
		Build(l,mid,rt<<1,A);Build(mid+1,r,rt<<1|1,A);
		Tree[rt]=max(Tree[rt<<1],Tree[rt<<1|1]);
		Cnt[rt]=Cnt[rt<<1]+Query(l+1,r,Tree[rt<<1],mid+1,r,rt<<1|1);
	}
	public : void Update(int ql,int qr,int val,int l,int r,int rt){
		if(ql<=l&&r<=qr){
			Tree[rt]=val;Cnt[rt]=1;
			return ;
		}
		int mid=(l+r)>>1;
		if(ql<=mid) Update(ql,qr,val,l,mid,rt<<1);
		if(qr>mid) Update(ql,qr,val,mid+1,r,rt<<1|1);
		Tree[rt]=max(Tree[rt<<1],Tree[rt<<1|1]);
		Cnt[rt]=Cnt[rt<<1]+Query(ql,qr,Tree[rt<<1],mid+1,r,rt<<1|1);
	}
	public : int Query(int ql,int qr,int maxx,int l,int r,int rt){
		if(Tree[rt]<=maxx) return 0;
		if(l==r) return 1;
		int mid=(l+r)>>1;
		if(Tree[rt<<1]<=maxx) return Query(ql,qr,maxx,mid+1,r,rt<<1|1);
		return (Cnt[rt]-Cnt[rt<<1])+Query(ql,qr,maxx,l,mid,rt<<1);
	}
};

int A[MAXN];
int n,m;
Segment Seg;

int main(){
    int T;scanf("%d",&T);
    while(T--){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i){
    		scanf("%d",&A[i]);
		}
		Seg.Build(1,n,1,A);
		for(int i=1;i<=m;++i){
			int x,val;scanf("%d%d",&x,&val);
			Seg.Update(x,x,val,1,n,1);
			printf("%d\n",Seg.Query(1,n,0,1,n,1));
			Seg.Update(x,x,A[x],1,n,1);
		}
	}
}

/*

2
10 5
8 3 1 3 12 3 4 6 9 2
1 2
2 3
3 4
4 5
7 8

9 10
2 1 4 3 4 6 3 8 3
4 5
4 7
2 5
2 7
2 8
2 1
3 5
3 7
3 8
3 1

*/

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值