scoi2015 bzoj4444 国旗计划

http://www.lydsy.com/JudgeOnline/problem.php?id=4444


其实我并没想到正解~~

首先把环拆成链,两倍链。

然后要离散化

f[i]表示左端点在i之前的最多能覆盖到哪个位置

可以先处理f[i]在i的时候最多能覆盖到哪里,然后向前缀和一样搞一下。


暴力计算出1-m的最小次数L,所有人的最小人数和这个数之差的绝对值不会超过1. ----------------------这是一个性质,因为假设把1覆盖的区间拿走,一定会用1-2条覆盖缺口。再加入必须的一条,可能使0-2条变得无用。   但是综合一下,波动不会超过2,毕竟求1的时候是最优的,覆盖会尽可能大。


我们考虑一个点i,如果能求出最少用几次到i+m之后,答案就出来了。而这就是可以用f[]这样搞出来的.

最直接的做法就是暴力枚举每个点i,一直跳f[i],看最少用几次到i+m之后,但这个时间复杂度明显很高。


由于有性质,我们可以对于每个点的跳跃次数从L-1开始跳(前提是我们知道当前点跳L-1会到哪里,如果知道,就算是暴力也可以优化很多(波动不超过2,接近On了))


现在问题就转化成:求每个点跳L-1次会到达哪个点u,然后从u暴力枚举一直跳到i+1之后。


我们发现从i到f[i]有点类似于一个链接(链表),假设每个i到f[i]建了一条边,构成森林。      我们会发现,不管从哪个点出发,最后都会到达2m.

也就是说,如果我们把整棵树倒过来,他就是一棵以2m为根的树。

然后我们dfs遍历整棵树,同时用栈维护从根到当前点u这条路径上的点(也就是它一直跳到2m的路径,然后对于每个点,就可以很快找到跳L-1次会到哪个点了,因为他要求的答案就在栈中。)


然后,就很好做了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=400000+20;
int n,m;
int read()
{
	int res=0;
	char c=getchar();
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())res=res*10+c-'0';
	return res;
}
struct edge
{
	int v,next;
}e[maxn<<1];
int head[maxn<<1];
int k;
void add(int u,int v)
{
	if(u==v)return ;
	e[k].v=v;
	e[k].next=head[u];
	head[u]=k++;
}
int det[maxn<<1];
int a[maxn][2];
int cnt,len,L;
int st[maxn];
int f[maxn<<1];//从i最多能到j 
int sta[maxn<<1];
int ans[maxn];
int top;
int find(int x)
{
	return lower_bound(det+1,det+len+1,x)-det;
}
void dfs(int u)
{
	sta[++top]=u;
	if(u<=m)for(int i=L;;i++)if(sta[top-i]>=u+m){
		ans[u]=i;
		break;
	}
	for(int i=head[u];~i;i=e[i].next)dfs(e[i].v);
	top--;
}
int main()
{
	memset(head,-1,sizeof(head));
	k=0;
	cnt=0;
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	{
		a[i][0]=read();
		a[i][1]=read();
		det[++cnt]=a[i][0];
		det[++cnt]=a[i][1];
	}
	sort(det+1,det+cnt+1);
	len=unique(det+1,det+cnt+1)-det-1;
	m=len;
	for(int i=1;i<=n;i++)
	{
		int x=find(a[i][0]),y=find(a[i][1]);
		st[i]=x;
		if(x<=y)
		{
			f[x]=max(f[x],y);
			f[x+m]=max(f[x+m],y+m);
		}
		else 
		{
			f[1]=max(f[1],y);
			f[x]=max(f[x],y+m);
			f[x+m]=max(f[x+m],2*m);
		}
	}
	for(int i=1;i<=2*m;i++)f[i]=max(f[i],f[i-1]);//维护前缀 
	L=-1;
	top=0;
	for(int i=1;i<=m;i=f[i])L++;
	for(int i=1;i<=2*m;i++)add(f[i],i);
	dfs(2*m);
	for(int i=1;i<=n;i++)printf("%d ",ans[st[i]]);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值