6847. 【2020.11.03提高组模拟】通往强者之路

63 篇文章 0 订阅
3 篇文章 0 订阅

Description



还是树荫下,蕾姆抱着丝碧卡,向你提出若干个询问。

Input

Output

Sample Input

5
6 3
7 6 6 7 7 5
7 8 10
4 5
4 4 4 3
4 5 6 7 10
6 3
6 5 7 6 6 6
6 9 10
4 6
3 4 3 3
14 19 15 4 9 16
5 6
4 4 4 5 4
28 59 55 46 99 34 

Sample Output

7 6 7
4 4 4 3 4
6 7 6
3 3 3 3 4 3
5 4 4 4 4 4


Data Constraint

Solution

先把a[ n ]算出来,(后面可能要用)若第 0 个为 n-1,第 n 个也为 n-1 ;否则第 n 个是 n。

如果把a分成长度为n+1的区间,则有:

比较第 i 个和第 i+1 个区间,可以发现以下规律:

  1. 每一个左边是 n 的 n-1 都向左移动了1步(和左边的那个数字交换位置,如果 n-1 下标为0,那么它会消失);
  2. 如果 n-1 左边是 n+1,那么它们两个就会变成n;
  3. 其它 n+1 和 n 都不变。

那么维护出 n-1 会和哪个 n+1 在什么时间相遇就好了。

 

如果把a分成长度为n的区间,可以发现不同的规律(令当前区间编号为 i ):

  1. 每一个 n+1 每次都会向右移动一步,如果它在区间 i 的末尾,它就会出现在 i+2 的开头,不会在 i+1 中出现;
  2. n+1 和 n-1 相遇会变成两个n;
  3. 其它 n-1 和 n 都不变。

由于这个 n+1 的消失实在鬼畜,我们不妨在每个区间末尾加上一个元素(初始为 n),那么第 x 个元素在一次变换后位置就是( x+1 ) mod ( n+1 )。

结合我比赛的做法,得到一种解法:

  1. 算出a_n(因为  \mod (n+1) 后有可能会访问到 a_n​);
  2. 算出每一个 n+1 在什么时候会和哪一个 n-1 相遇,这个O(n) 扫一下就好了,相遇时间为len*n(令len为它们之间的长度,其实这个时间表示的是它们消失的那个区间的开头)。注意,这里要用单调栈维护
  3. 给会和 n-1 相遇的 n+1 按相遇时间排序,给询问排序;
  4. 对于每一个询问x,先把会消失的n+1,n-1全部变成 n,然后考虑当前位置上的数是什么:
    1). 若a_{x\mod{n}}=n-1,说明它是 n-1(因为 n-1 是不动的,而且这个 n-1 还没有消失);
    2). 若a_{x\mod{(n+1)}}=n+1,说明它是 n+1 (因为每次a[ i ]位置上是2的下一次移动到了a[ i +n+1 ]的位置上);
    3). 否则它就是 n 。

时间复杂度O(n\log_2 n)

 

Code 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b)  for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 100004
using namespace std;
ll x,l[N];
I T,n,m,a[N<<1],st[N],tp,cnt,ans[N];
struct node{ll x;I id;}q[N],p[N];
void R(){
	x=0;I w=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	x*=w;
}
I cmp(node a,node b){return a.x<b.x;}
I main(){
	freopen("way.in","r",stdin);
	freopen("way.out","w",stdout);
	R();T=x;
	while(T--){
		mem(a,cnt=tp=0),mem(l,255);
		R(),n=x,R(),m=x;
		F(i,0,n-1) R(),a[i]=x;a[n]=n-1+(a[0]>n-1);
		F(i,1,m) R(),q[i]=node{x,i};
		Fd(i,n-1,0){
			if(a[i]==n-1) st[++tp]=i;
			if(a[i]==n+1&&tp) l[i]=st[tp--];
		}
		Fd(i,n-1,0) if(l[i]==-1&&a[i]==n+1&&tp) l[i]=st[tp--];
		F(i,0,n-1) if(l[i]!=-1){p[++cnt]=node{(l[i]>i?l[i]-i:l[i]-i+n+1)*n,i};}
		sort(q+1,q+1+m,cmp);
		sort(p+1,p+1+cnt,cmp);
		I j=1;
		F(i,1,m){
			while(j<=cnt&&p[j].x<=q[i].x) a[p[j].id]=a[l[p[j].id]]=n,j++;
			if(a[q[i].x%n]==n-1) ans[q[i].id]=n-1;
			else ans[q[i].id]=n+(a[q[i].x%(n+1)]==n+1);
		}
		F(i,1,m) printf("%d ",ans[i]);
		printf("\n");
	}
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值