AtCoder Grand Contest 012 E Camel and Oases 状压dp

36 篇文章 0 订阅
21 篇文章 0 订阅

Description


有一个容量为V的包,n个接水点,坐标分别为x[]
有两种移动方式:

  1. 若两个接水点之间的距离不超过此时包的容量v,那么就可以移动
  2. 若此时v不等于0,那么可以使v=v/2(下取整),然后跳到任意一个位置

对于每一个接水处作为出发点,分别回答能否到达所有其余接水点
n , V ≤ 1 0 5 n,V\le 10^5 n,V105
∀ i ≤ n ,    x i ≤ 1 0 9 \forall i\le n,\; x_i\le10^9 in,xi109

Solution


操作2最多只有logV次,而同一个v能走到的位置是若干不相交的线段
问题变成:我们在V最大的时候强制选一个线段,每一个v最多选一个线段,问能否选出一个线段的集合覆盖整个数轴

考虑状压dp,设f[x]表示选择v状态为x的时候,从1起能覆盖到的最右边,g[x]表示从n起能覆盖到的最左边,当f[x]和g[x]在某一个V的线段内部时就是可行的

一个小trick就是记录left[j]表示j往右最多到哪里,这样转移就非常方便了。最后统计答案的时候要注意一下,一开始写萎T掉了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const int N=400005;

int f[N],g[N],p[N],L[N],R[N],ans[N];
int left[25][N],righ[25][N],bel[N];
int n,V,tot;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void pre(int v,int id) {
	for (int i=1,j;i<=n;i=j+1) {
		for (j=i;j<n&&p[j+1]-p[j]<=v;) j++;
		rep(k,i,j) left[id][i]=j,righ[id][j]=i;
	}
}

void solve() {
	for (int i=0,lim=(1<<tot);i<lim;++i) {
		g[i]=n+1;
	}
	for (int i=0,lim=(1<<tot);i<lim;++i) {
		rep(j,0,tot-1) if ((i>>j)&1) {
			f[i]=std:: max(f[i],left[j][f[i-(1<<j)]+1]);
			g[i]=std:: min(g[i],righ[j][g[i-(1<<j)]-1]);
		}
	}
	for (int i=0,lim=(1<<tot);i<lim;++i) {
		int j=lim-i-1;
		if (f[i]+1>=L[bel[f[i]+1]]&&g[j]-1<=R[bel[f[i]+1]]) {
			ans[bel[f[i]+1]]=1;
		}
	}
}

int main(void) {
	fill(righ,63);
	n=read(),V=read();
	rep(i,1,n) p[i]=read();
	for (int v=V/2;;v>>=1) {
		pre(v,tot++);
		if (!v) break;
	}
	for (int i=1,j;i<=n;i=j+1) {
		bel[i]=++bel[0];
		for (j=i;j<n&&p[j+1]-p[j]<=V;) bel[++j]=bel[0];
		L[bel[0]]=i,R[bel[0]]=j;
	}
	solve();
	rep(i,1,n) if (ans[bel[i]]) puts("Possible");
	else puts("Impossible");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值