patrick[unLOCK]

Online Judge:20191010 长沙一中 CSP模拟赛#day1

Label:好题,思维题,树状数组

题目描述

派大星的家门前有一条河(请不要向出题人提问海底为什么会有河),派大星每天要观察这条河,并且统计河中岛屿的个数。

河床的地形可以抽象为一个长度为\(n\)的数列{\(ai\)},第i位的数字代表河床对应位置的高度。

当水位为h 时,所有高度低于h 的位置都会被水覆盖,高度大于等于h 的地形就露出水面,连成了岛屿。

比如当{\(ai\)}= {\(5,3,2,6,1\)},且\(h=3\)时,河中共有2个岛屿,分别为{\(5,3\)} 和{\(6\)}。

请你协助派大星统计每一天的岛屿的个数。河床每个位置的高度可能会发生变化,而且水位也会发生变化。

输入

输入文件为patrick.in。
第一行两个正整数N;M,表示数列的长度,和询问的个数。
第二行N 个正整数Hi,表示一开始的数列,即一开始河床在每处的高度。
接下来M 行,每行有如下两种可能的格式:

  1. Q 外加一个整数C,表示询问若将水位抬高或降低到C^Last,湖中会有几个岛。
  2. C 外加两个整数A;B,表示A^Last 处的高度变为了B^Last。其中Last 表示上一次询问(Q
    操作) 的答案。Last 一开始等于0,且^是按位异或的意思。

输出

输出文件为patrick.out。
共M 行,每行一个整数,代表每次询问的答案。

样例

Input#1

5 7
3 2 1 3 5
Q 1
Q 2
Q 3
C 2 2
C 5 3
C 4 2
Q 2

Output#1

1
2
1
3

Input#2

15
68 42
1 35
25 70
59 79
65 63
46 6
28 82
92 62
43 96
37 28
5 92
54 3
83 93
17 22
96 19

Output#2

1
2
1    
3

询问在解码后分别为1; 3; 1; 3。最后一次询问前的数列为3; 2; 3; 2; 3。

Hint

保证通过异或之后,所有输入数据及操作数值的范围都在\([1,500000]\)之内。

测试点信息

对于测试点1/2/3,\(N,M<=5000\)
对于测试点4/5/6,\(N,M<=5*10^5\),保证没有C操作;
对于测试点7/8,\(N,M<=10^5\)
对于测试点9/10,\(N,M<=5*10^5\)

题解

Substack1

每个询问暴力\(O(N)\)统计一遍,时间复杂度\(O(N\cdot M)\),可以通过测试点1/2/3。

Substack2

算法一

没有C操作。直接预处理出\(ans[1..maxn]\),对于每个询问\(O(1)\)回答。

按河床的高度,从高到低添加,利用打标记的方式维护当前岛屿数。

时间复杂度为\(O(N)\)。结合Substack1可以通过前6个测试点。

代码如下:

#include<bits/stdc++.h>
using namespace std;
bool nc1;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
const int N=5e5+10;
int a[N],n,m;
namespace p30{
	inline int Ask(int h){
		int num=0;
		a[0]=a[n+1]=-1;
		for(register int i=1;i<=n+1;++i)if(a[i-1]>=h&&a[i]<h)++num;
		return num;
	}
	void solve(){
		int lst=0;
		while(m--){
			char op[3];scanf("%s",op);
			if(op[0]=='Q'){
				int x=read();
				lst=Ask(x^lst);
				printf("%d\n",lst);
			}	
			else{
				int A=read(),B=read();
				A^=lst,B^=lst;
				a[A]=B;
			}
		}
	}
}
namespace p60{
	int mx=0,id[N],ans[N];
	bool mark[N],boom[N];
	inline bool cmp(int p,int q){return a[p]>a[q];}
	void solve(){
		for(register int i=1;i<=n;++i)id[i]=i,mx=max(mx,a[i]);
		sort(id+1,id+n+1,cmp);
		
		int num=0;
		for(register int i=1;i<=n;++i){
			int x=id[i],nowh=a[x];
			mark[x]=1;
			if(mark[x-1]&&mark[x+1])num--;
			else if(!mark[x-1]&&!mark[x+1])num++;
			ans[nowh]=num;boom[nowh]=1;
		}
		for(register int i=mx;i>=0;--i)if(!boom[i]){
			ans[i]=ans[i+1];
		}
		int lst=0;
		while(m--){
			char op[3];scanf("%s",op);
			if(op[0]=='Q'){
				int x=read();x^=lst;
				lst=ans[x];
				printf("%d\n",lst);
			}	
		}		
	}
}
bool nc2;
int main(){
//	cout<<(&nc2-&nc1)/1024/1024<<endl;
//	freopen("patrick.in","r",stdin);
//	freopen("patrick.out","w",stdout);
	n=read(),m=read();
	for(register int i=1;i<=n;++i)a[i]=read();
	if(n<=5000&&m<=5000){p30::solve();return 0;}
	p60::solve();
}

算法二

其实是为后面的100pts做法准备的。

算法一是通过,逆着一个个加块的方式来求的,有没有什么方法能够一遍扫过去,求得所有水位下的对应答案呢?

通过yy可以想到下面的做法:对于当前河床\(x\),只考虑\(x\)\(x+1\)的相对关系,假如存在\(a[x]>a[x+1]\),我就将\([a[x+1]+1,a[x]]\)这个区间内的水位会形成的岛屿数+1。因为在\(x,x+1\)这里形成了一个断层,x往前一段都会在水位为\([a[x+1]+1,a[x]]\)时形成一块岛屿。

区间加可以用差分完成,统计时利用前缀和。时间复杂度为\(O(N)\)

Substack3

根据算法二不难想到正解,只要利用树状数组修改即可(区间修改,单点查询)。当更改\(x\)的高度时,先回撤{\(x-1,x\)},{\(x,x+1\)}对答案的影响,再给\(a[x]\)赋上新的值,重新计算一遍{\(x-1,x\)},{\(x,x+1\)}对答案的影响。

综上时间复杂度为\(O(NlogN)\)

完整代码如下:

#include<bits/stdc++.h>
#define N 500010
using namespace std;
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
int a[N],n,m;
struct Bit{
	int c[N];
	void add(int x,int d){while(x<N)c[x]+=d,x+=x&-x;}
	int ask(int x){
		int res=0;
		while(x)res+=c[x],x-=x&-x;
		return res;
	}
	void update(int l,int r,int d){add(l,d),add(r+1,-d);}
}B;
void BOOM(int x,int d){
	if(x!=1&&a[x-1]>a[x])B.update(a[x]+1,a[x-1],d);
	if(a[x]>a[x+1])B.update(a[x+1]+1,a[x],d);
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)if(a[i]>a[i+1])B.update(a[i+1]+1,a[i],1);
	int lst=0;
	while(m--){
		char op[3];scanf("%s",op);
		if(op[0]=='Q'){
			int x=read()^lst;
			printf("%d\n",lst=B.ask(x));
		}
		else{
			int x=read()^lst,d=read()^lst;
			BOOM(x,-1);a[x]=d;BOOM(x,1);	
		}
	}	
}
posted @ 2019-10-11 13:46  real_lyb  阅读(7)  评论(0编辑  收藏  举报