您的位置:首页 > 其它

scoi2015 bzoj4444 国旗计划

2016-04-01 15:57 375 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bzoj 序列