AT2365-[AGC012E]Camel and Oases【状压dp】

正题

题目链接:https://www.luogu.com.cn/problem/AT2365


题目大意

一个数轴上有(n)个点,开始你有个水壶容量为(V),你每次有两个操作

  • 走到一个距离与你不超过(V)的点
  • (V=lfloorfrac V2 floor),然后跳到任意一个点。

对于每个点求从这个点出发能否走到所有点。

(1leq n,Vleq 2 imes 10^5,-10^9leq x_ileq 10^9)


解题思路

显然操作二的次数不会超过(log V),考虑从这里入手。考虑有没有一种方案能够把序列分成若干段,每一段相邻的差不超过给其匹配的一个(lfloorfrac{V}{2^x} floor)(V)的段刚好在起点。

并且因为(n)很大我们可以考虑改成用数字而不是下标维护它这一维,设(f_{S})表示用集合(S)中的(V)能够从右边走到的最左边的位置,同理(g_S)表示从左边开始走,然后枚举总间段。

注意到这样的复杂度是(O(nV))的,不过如果一段的左右都已经分出了(log V)个段,那么这个位置也是不合法的(因为)。

时间复杂度:(O(Vlog V))


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10,M=19;
int n,m,V,w[M],x[N],l[M][N],r[M][N],f[1<<M],g[1<<M],ans[N];
int main()
{
	scanf("%d%d",&n,&V);w[0]=V;
	while(V){V>>=1;w[++m]=V;}
	swap(w[0],w[m]);
	for(int i=1;i<=n;i++)scanf("%d",&x[i]);
	for(int j=0;j<=m;j++){
		l[j][1]=1;r[j][n]=n;
		for(int i=2;i<=n;i++)
			l[j][i]=(x[i]-x[i-1]<=w[j])?l[j][i-1]:i;
		for(int i=n-1;i>=1;i--)
			r[j][i]=(x[i+1]-x[i]<=w[j])?r[j][i+1]:i;
	}
	int MS=(1<<m);
	for(int s=0;s<MS;s++){
		f[s]=n+1;
		for(int i=0;i<m;i++){
			if(!((s>>i)&1))continue;
			f[s]=min(f[s],l[i][f[s^(1<<i)]-1]);
			g[s]=max(g[s],r[i][g[s^(1<<i)]+1]);
		}
	}
	int cnt=0;
	for(int i=1;i<=n;i++){
		bool flag=0;
		for(int s=0;s<MS;s++){
			int t=(MS-1)^s;
			if(g[s]>=i-1&&f[t]<=r[m][i]+1)
			{flag=1;break;}
		}
		for(int j=i;j<=r[m][i];j++)
			ans[j]|=flag;
		cnt++;i=r[m][i];
		if(cnt>m)break;
	}
	cnt=0;
	for(int i=n;i>=1;i--){
		bool flag=0;
		for(int s=0;s<MS;s++){
			int t=(MS-1)^s;
			if(f[s]<=i+1&&g[t]>=l[m][i]-1)
			{flag=1;break;}
		}
		for(int j=i;j>=l[m][i];j--)
			ans[j]|=flag;
		cnt++;i=l[m][i];
		if(cnt>m)break;
	}
	for(int i=1;i<=n;i++)
		if(ans[i])puts("Possible");
		else puts("Impossible");
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/15497446.html