POI2014 Around the world

Around the world

POI2014
从未见过如此无耻的题目——既卡时间又卡空间

题意

1.在一个圆上有n个飞机场

2.L[i]是第i个与第i+1个机场之间的距离
(L[n]是第n个和第1个之间的距离)

3.有s个询问,每个询问有一个d[i]

4.对于每个询问:起点任选,每次飞行的距离不能超过d[i],绕这个圆一圈至少要飞多少次

方案1(并查集)

1.从左往右,每个点所能到达的最远是单调不降的
2.断环成链,将链延长到两倍
3.预处理出每个点到达的最远点(尺取)
4.枚举起点x
5.求最少几次可以跳到>=x+n的点
用并查集往上跳,找到第一个par[x]>=i+n的点,然后把经过的点全部指向par[x]
因为从左往右,每个点要跳到的点也是单调后移的,即这些点只会往后面合并,这就保存并查集的合并均摊是O(n)的

具体代码

//因为被卡了内存,于是去掉了万能头文件
//因为被卡了时间,于是加了O3优化
#pragma GCC optimize(3)
#include<cstdio>
#include<algorithm>
using namespace std;
const int M=1000005;
int n,S,D;
int dis[M],par[M*2],stk[M],val[M*2],top;
int Dis(int x) {
	if(x<=n)return dis[x];
	return dis[x-n];
}
void solve() {
	int L=1,R=1;
	int sum=0;
	for(int i=1; i<=n+n; i++)par[i]=i,val[i]=0;
	while(L<=n+n) {
		while(R<=n+n&&sum+Dis(R)<=D) {
			sum+=Dis(R);
			R++;
		}
		par[L]=R;
		if(par[L]!=L)val[L]=1;
		sum-=Dis(L);
		L++;
	}
	int ans=1e9;
	for(int i=1; i<=n; i++) {
		int x=i;
		top=0;
		bool flag=1;
		while(1) {
			if(par[x]==x) {
				flag=0;
				break;
			}
			stk[++top]=x;
			if(par[x]>=i+n)break;
			x=par[x];
		}
		while(top>1) {
			val[stk[top-1]]+=val[stk[top]];
			par[stk[top-1]]=par[x];
			top--;
		}
		if(flag)ans=min(ans,val[i]);
	}
	if(ans==1e9)printf("NIE\n");
	else printf("%d\n",ans);
}
int main() {
	scanf("%d %d",&n,&S);
	int mx=0;
	for(int i=1; i<=n; i++) {
		scanf("%d",&dis[i]);
		mx=max(dis[i],mx);
	}
	for(int i=1; i<=S; i++) {
		scanf("%d",&D);
		if(D<mx)printf("NIE\n");
		else solve();
	}
	return 0;
}
方案2(贪心)

1.先断环成链,计算前缀和

2.依次找n~2*n的点向前最远可以到哪里
一遍扫过去,while(dis[i]-dis[pos]>D)pos++,因为越后面,向前的最远点必然也后移

3.每次每次把i点向前并到pre[pos]里,并且贡献+1,

4.找到第一个i-pre[i]>=n即可

具体代码

#include<cstdio>
#include<algorithm>
using namespace std;
const int M=1000005;
int n,S,D;
int dis[M*2],pre[M*2],val[M*2];
void solve() {
	for(int i=1;i<=n*2;i++)val[i]=0,pre[i]=i;
	int pos=0;
	for(int i=n;i<=n*2;i++){
		while(dis[i]-dis[pos]>D)pos++;
		pre[i]=pre[pos];
		val[i]=val[pos]+1;
		if(i-pre[i]>=n){
			printf("%d\n",val[i]);
			return;
		}
	}
}
int main() {
	scanf("%d %d",&n,&S);
	int mx=0;
	for(int i=1; i<=n; i++) {
		scanf("%d",&dis[i]);
		mx=max(dis[i],mx);
		dis[i+n]=dis[i];
	}
	for(int i=1;i<=n*2;i++){
		dis[i]+=dis[i-1];
	}
	for(int i=1; i<=S; i++) {
		scanf("%d",&D);
		if(D<mx)printf("NIE\n");
		else solve();
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值