【BZOJ4444】国旗计划

Description

  
   题目链接
  
  
  

Solution

磕了3个半小时没做出来的题,就是全场崩.
  
   首先对于一个人的答案是很好求的,显然是选择左端点在此人区间中,右端点最远(最靠右)的人作为下一个接棒人.因此用线段树维护一下就可以了,注意判断跨环边界的情况(n->1).
  
   时间复杂度$ \mathcal O(n^2 \log n) $.
  
   接着我再想,如果按左端点从大到小从右往左注意处理,每个人其实就是选择那些左端点处于自己区间中的人作为后继,选择它们的答案最小值即可.然而终止点也是不断移动的,所以每个人的答案在之后的人看是动态的.考虑很难修改,就猜性质,猜了个性质,用2h实现程序暴力和数据生成器,然后一组数据见祖宗.
  
   TMD

  
   稍微换一下思路,与其想办法维护接力方案,不如直接考虑计算每个人,并优化这个过程.
  
   可以发现,每个人选择哪一个人作为下一棒是唯一确定的(左端点在区间内的人中,右端点最远的那个人). 且每传一次棒,贡献的距离也是唯一确定的(当前人的右端点与下一棒的右端点的距离).
  
   确定下一棒可以用线段树直接模拟.
  
   对于每个人\(i\),我们相当于要知道从他开始至少传多少棒之后,总距离贡献超过了\(m-len_i\). 其中\(len_i\)表示这个人的区间长度减一.
  
   那么倍增出传\(2^k\)步后是谁,以及总距离贡献是多少.
  
   对于每个人直接从大到小枚举\(k\)逐步逼近目标步数即可.
  
   总时间复杂度是\(\mathcal O (n \log n)\)
  
  
  

Code

  

#include <cstdio>
#include <algorithm>
#define mp make_pair
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=200005;
const int INF=1e9;
int n,m;
int diz[N*2],dcnt,who[N*2];
int go[N][19];
ll d[N][19];
struct Man{
	int l,r,id;
	int get(){
		if(l<r) return r-l;
		return r+(dcnt-l);
	}
}p[N];
void readData(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&p[i].l,&p[i].r);
		p[i].id=i;
	}
}
void Diz(){
	for(int i=1;i<=n;i++){
		diz[++dcnt]=p[i].l; 
		diz[++dcnt]=p[i].r;
	}
	sort(diz+1,diz+1+dcnt);
	dcnt=unique(diz+1,diz+1+dcnt)-diz-1;
	for(int i=1;i<=n;i++){
		p[i].l=lower_bound(diz+1,diz+1+dcnt,p[i].l)-diz;
		p[i].r=lower_bound(diz+1,diz+1+dcnt,p[i].r)-diz;
	}
}
bool cmpMan(const Man &a,const Man &b){
	return a.l<b.l;
}
pii max(pii x,pii y){
	return x.first>y.first?x:y;
}
namespace SEG{/*{{{*/
	const int S=N*4;
	int rt,sz;
	int ch[S][2];
	pii info[S];
	inline void pushup(int u){
		info[u]=max(info[ch[u][0]],info[ch[u][1]]);
	}
	void build(int &u,int l,int r){
		u=++sz;
		if(l==r){
			info[u]=mp(-INF,-INF);
			return;
		}
		int mid=(l+r)>>1;
		build(ch[u][0],l,mid);
		build(ch[u][1],mid+1,r);
		pushup(u);
	}
	void modify(int u,int l,int r,int pos,pii val){
		if(l==r){
			info[u]=val;
			return;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)
			modify(ch[u][0],l,mid,pos,val);
		else
			modify(ch[u][1],mid+1,r,pos,val);
		pushup(u);
	}
	pii query(int u,int l,int r,int L,int R){
		if(L<=l&&r<=R) return info[u];
		int mid=(l+r)>>1;
		if(R<=mid) return query(ch[u][0],l,mid,L,R);
		else if(mid<L) return query(ch[u][1],mid+1,r,L,R);
		else
			return max(query(ch[u][0],l,mid,L,mid),query(ch[u][1],mid+1,r,mid+1,R));
	}
}/*}}}*/
void calc(){
	SEG::build(SEG::rt,1,dcnt);
	for(int i=1;i<=n;i++)
		SEG::modify(SEG::rt,1,dcnt,p[i].l,mp(p[i].l>p[i].r?p[i].r+dcnt:p[i].r,i));
	for(int i=1;i<=n;i++){
		int x;
		pii get;
		if(p[i].l<p[i].r)
			get=SEG::query(SEG::rt,1,dcnt,p[i].l+1,p[i].r);
		else{
			get=mp(-INF,-INF);
			if(p[i].l<dcnt)
				get=SEG::query(SEG::rt,1,dcnt,p[i].l+1,dcnt);
			pii tmp=SEG::query(SEG::rt,1,dcnt,1,p[i].r);
			tmp.first+=dcnt;
			get=max(get,tmp);
		}
		go[i][0]=get.second;
		d[i][0]=get.first-(p[i].l>p[i].r?p[i].r+dcnt:p[i].r);
	}
	for(int j=1;j<=18;j++)
		for(int u=1;u<=n;u++){
			go[u][j]=go[go[u][j-1]][j-1];
			d[u][j]=d[u][j-1]+d[go[u][j-1]][j-1];
		}
}
void solve(){
	for(int i=1;i<=n;i++){
		int ans=1;
		int u=i,last=dcnt-p[i].get();
		for(int j=18;j>=0;j--)
			if(d[u][j]<last){
				last-=d[u][j];
				ans+=(1<<j);
				u=go[u][j];
			}
		printf("%d ",ans+1);
	}
	puts("");
}
int main(){
	readData();
	Diz();
	calc();
	solve();
	return 0;
}
posted @ 2018-08-22 15:53  RogerDTZ  阅读(186)  评论(0编辑  收藏  举报