zoukankan      html  css  js  c++  java
  • 4.19 省选模拟赛 跳跃 倍增 二分 线段树 建图

    LINK:跳跃

    avatar
    avatar

    不算难想的题目 考试的时候没想出来 还是想的太少 思路被束缚住了。

    第一个想法 二分 发现check的时候还是需要枚举点对来算距离什么的 然后弃掉。

    计算过样例后发现一个点到达右边可能先去左边再一下子跳到右边。

    直接建图bfs。

    发现这样做是n^3的 直接线段树优化建图了。 复杂度n^2log.

    const int MAXN=3010;
    int n,maxx,len,cnt,root,id;
    int a[MAXN];
    struct wy{int l,r;}t[MAXN<<2];
    int b[MAXN][MAXN],vis[MAXN*MAXN],dis[MAXN*MAXN];
    int lin[MAXN*MAXN],ver[MAXN*MAXN],nex[MAXN*MAXN],e[MAXN*MAXN];
    inline void add(int x,int y,int z)
    {
    	ver[++len]=y;
    	nex[len]=lin[x];
    	lin[x]=len;
    	e[len]=z;
    }
    inline void build(int &p,int l,int r)
    {
    	if(!p)p=++cnt;
    	if(l==r){add(p,l,1);return;}
    	int mid=(l+r)>>1;
    	build(l(p),l,mid);
    	build(r(p),mid+1,r);
    	add(p,l(p),0);add(p,r(p),0);
    }
    inline void change(int p,int l,int r,int L,int R,int x)
    {
    	if(L<=l&&R>=r)
    	{
    		add(x,p,0);
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(L<=mid)change(l(p),l,mid,L,R,x);
    	if(R>mid)change(r(p),mid+1,r,L,R,x);
    	return;
    }
    deque<int> q;
    inline void bfs(int w)
    {
    	++id;q.pf(w);dis[w]=0;vis[w]=id;
    	while(q.size())
    	{
    		int x=q.front();
    		q.popf();
    		go(x)
    		{
    			if(vis[tn]!=id)
    			{
    				vis[tn]=id;
    				dis[tn]=dis[x]+e[i];
    				if(!e[i])q.pf(tn);
    				else q.pb(tn);
    			}
    		}
    	}
    }
    int main()
    {
    	//freopen("jumping.in","r",stdin);
    	//freopen("jumping.out","w",stdout);
    	get(n);
    	if(n<=3000)
    	{
    		cnt=n;build(root,1,n);
    		for(int i=1;i<=n;++i)
    		{
    			int L,R;get(a[i]);
    			L=max(i-a[i],1);
    			R=min(i+a[i],n);
    			change(root,1,n,L,R,i);
    		}
    		rep(1,n,i)
    		{
    			bfs(i);
    			rep(1,n,j)b[i][j]=dis[j];
    		}
    		int ans=0;
    		rep(1,n,i)rep(1,i,j)ans=max(ans,min(b[i][j],b[j][i]));
    		put(ans);return 0;
    	}
    	return 0;
    }
    

    我发现除了枚举点对之外没有什么好方法 剩下的时间就在研究无向图的直径/有相图的直径 下界复杂度都是nm的 放弃治疗。

    还是考虑二分 二分出答案之后 我们需要得到两个点对之间跳mid步都达不到对方。

    (L_{i,j})表示i这个点跳j步所能到达最左端的地方。

    容易发现这个东西可能可以反复横跳 所以还需要一个数组 (R_{i,j})表示i这个点跳j步所能达到最右端的地方。

    转移显然。

    现在有两个问题需要解决一个是L R数组如何快速求出 一个是如何判定答案。

    考虑前者 容易想到倍增。正确性显然 这样就可以在nlog^2的时间内求了 注意利用线段树维护 好写一点。

    考虑后者 如果答案还可以更大的话 那么必然两个点x,y(x<y) 满足x跳mid步到最右端<y y跳mid步到最左端>x.

    这样check即可。

    不过可以发现对于二分一个mid,log.我们需要先用倍增数组拼成mid log.拼一次nlog.

    复杂度nlog^3. 一个小trick 直接倍增求答案 然后这样就省掉了二分。复杂度nlog^2.

    注意 判定答案的时候 一定要严格>和< 如果不严格 可能对于边界 多跳若干部也还是边界等等。

    const int MAXN=200010;
    int n,ans;
    struct wy{int l,r,sum;}t[MAXN<<2];
    int a[MAXN],Log[MAXN],w[MAXN],bl[MAXN],br[MAXN],c[MAXN];
    int L[MAXN][20],R[MAXN][20],ansl[MAXN],ansr[MAXN],wl[MAXN],wr[MAXN];
    inline void build(int p,int l,int r)
    {
    	l(p)=l;r(p)=r;
    	if(l==r){sum(p)=w[l];return;}
    	int mid=(l+r)>>1;
    	build(zz,l,mid);
    	build(yy,mid+1,r);
    	sum(p)=min(sum(zz),sum(yy));
    }
    inline int ask(int p,int l,int r)
    {
    	if(l<=l(p)&&r>=r(p))return sum(p);
    	int mid=(l(p)+r(p))>>1;
    	if(r<=mid)return ask(zz,l,r);
    	if(l>mid)return ask(yy,l,r);
    	return min(ask(zz,l,r),ask(yy,l,r));
    }
    inline int check()
    {
    	fep(n,1,i)c[i]=max(c[i+1],wl[i]);
    	rep(1,n,i)if(wr[i]!=n&&c[wr[i]+1]>i)return 1;
    	return 0;
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	get(n);Log[0]=-1;
    	rep(1,n,i)get(a[i]),ansl[i]=ansr[i]=i,L[i][0]=max(1,i-a[i]),R[i][0]=min(n,i+a[i]),Log[i]=Log[i>>1]+1;
    	rep(1,Log[n],j)
    	{
    		rep(1,n,i)w[i]=L[i][j-1];
    		build(1,1,n);
    		rep(1,n,i)L[i][j]=ask(1,L[i][j-1],R[i][j-1]);
    		rep(1,n,i)w[i]=-R[i][j-1];
    		build(1,1,n);
    		rep(1,n,i)R[i][j]=-ask(1,L[i][j-1],R[i][j-1]);
    	}
    	fep(Log[n],0,j)//形成答案集合.
    	{
    		rep(1,n,i)bl[i]=ansl[i],br[i]=ansr[i];
    		rep(1,n,i)w[i]=L[i][j];
    		build(1,1,n);
    		rep(1,n,i)wl[i]=ask(1,bl[i],br[i]);
    		rep(1,n,i)w[i]=-R[i][j];
    		build(1,1,n);
    		rep(1,n,i)wr[i]=-ask(1,bl[i],br[i]);
    		if(check())
    		{
    			rep(1,n,i)ansl[i]=wl[i],ansr[i]=wr[i];
    			ans=ans|(1<<j);
    		}
    	}
    	put(ans+1);
    	return 0;
    }
    
  • 相关阅读:
    温故而知新 js 点击空白处关闭气泡
    javascript 打印错误信息 catch err
    ajax application/json 的坑
    nodejs 的好基友:pm2
    有道翻译 / 百度翻译Api
    PHP 正则表达式
    php 正则替换
    github get 请求指定页面的代码
    H5 input 聚焦 置顶
    autoHotKey 一些脚本积累
  • 原文地址:https://www.cnblogs.com/chdy/p/12738391.html
Copyright © 2011-2022 走看看