zoukankan      html  css  js  c++  java
  • [机房测试]11.5

    [机房测试]11.5

    各种被卡。。。不过ssw02状态倒是回来了

    欢迎转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11799831.html


    a

    数学题。

    30分做法:1e6的大小范围,DP即可。(不判越界20分)

    满分做法:枚举 b 的n次方,把原题转化为 T = b^n S + ma 的形式,这样 n 就有只有 log 种取值枚举即可。然后把 m 转化为一个 b 进制的数,贪心选取 ∑xi 即可。 总次数即为 min { n + ∑xi }

    b

    输入给出链长 N , 和 M 个点对( x,y ) , 询问最长距离的最短值。

    先讲一下60分(可以卡到70,吸氧可以过),最坏复杂度 NM2logN

    答案具有二分性,转化为判定性问题。

    考虑到每次二分的答案lim , 把所有区间长 > lim 的区间拿出来 , 在N上枚举固定左端点L , 那么对于每一个区间, R可以所在的有效区间必定是一段( i > mid 就是固定右端点 ,反正是固定一个点 ) 。 这些有效区间必须有一个公共交点,否则这个点位不合法。如果所有点位都不合法,返回false , 否则返回 true 。

    考虑到优化一下, 枚举的点位可以预处理出来,只有一部分点位可以提供来check , 这个优化可以使复杂度将至一个随机数据下只用跑 2s 的时间消耗,吸吸氧再加点杂优化就可以水过( ltw自带小常数,也过了 )

    70代码:

    #include<bits/stdc++.h>
    using namespace std ;
    const int MAXN = 100005 ;
    inline int read(){
    	int s = 0 ; char g=getchar() ; while( g>'9'||g<'0' )g=getchar() ; 
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
    }
    int N , M ; 
    struct ap{
    	int l , r , len ;
    }t[MAXN],p[MAXN];
    inline bool cmp( ap x , ap y ){
    	return x.len > y.len ; 
    }
    inline bool check( int lim ){
    	int now = 0 ; 
    	while( now < N ){
    		if( t[now+1].len <= lim )break ;
    	    else now++ ;
    	}
    	if( now == 0 || now == 1 )return true ; 
    	int L = 1 , R = N  ; 
    	for( register int i = 1 ; i <= now ; ++i ){
    		if( t[i].l-lim > R || t[i].r+lim < L )return false ;
    		L = max( L , t[i].l-lim ) , R = min( R , t[i].r+lim ) ; 
    	}
    	if( L > R )return false ;
    	for( register int i = L ; i <= R ; ++i ){//枚举点位 
    	    int flag = true ; int  LL = 1 , RR = N ;
    		for( register int j = 1 ; j <= now ; ++j ){//枚举限制 
    			int mid = ( t[j].l+t[j].r )>>1 ; 
    			if( i <= mid ){
    				int k = lim - abs( t[j].l - i ) ; if( k < 0 ){flag = false ; break ; } //这个点位不合法 
    				int xl = t[j].r-k  , xr = t[j].r + k  ; 
    				if( xl > RR || xr < LL ){flag = false ; break ;}
    				LL = max( LL , xl ) , RR = min( RR , xr ) ; 
    			}
    			else{
    				int k = lim - abs( t[j].r - i ) ; if( k < 0 ){flag = false ; break ; } //这个点位不合法 
    				int xl = t[j].l-k , xr = t[j].l +k  ; 
    				if( xl > RR || xr < LL ){flag = false ; break ;}
    				LL = max( LL , xl ) , RR = min( RR , xr ) ; 
    			}
    		}
    		if( flag ){
    		    //cout<<i<<endl ;
    			return true; 
    		}
    	}
    	return false ;
    }
    void  dx( int l , int r ){
    	int ans = t[1].len ;
    	while( l <= r ){
    		int mid = (l+r)>>1 ; 
    		if( check(mid) )r=mid-1,ans=mid ; 
    		else l = mid+1 ; 
    	} 
    	cout<<ans ; 
    }
    int main(){
    	freopen("b.in","r",stdin) ; 
    	freopen("b.out","w",stdout) ;
    	N = read() , M = read() ; 
    	if( M <= 1 ){cout<<0;return 0;}
    	for( int i = 1 ; i <= M ; ++i )
    	    t[i].l=read(),t[i].r=read(),t[i].len = t[i].r-t[i].l ; 
    	sort( t+1 , t+M+1 , cmp ) ;
    	dx( 1 , 100005 ) ;
    	return 0 ;  
    }
    

    正解:这是一道很巧妙的曼哈顿距离题目 。

    经典套路!!!!

    在二分答案lim后,我们建立一个以 L 为 x 轴,R 为 y 轴的平面直角坐标系 , 对于任意一个点对 ( x , y ) , 点对 ( u , v )到这个点对的满足建立0边可到达的意义就是坐标系上曼哈顿距离 <= lim ,所有曼哈顿距离小于等于 lim 的点对是一个斜45度的正方形 。

    问题转化为多个矩形判断是否同时存在一个交点 。 用线性规划知识将其旋转45度即可 。(pair<>还可以这样用?)

    #include<bits/stdc++.h>
    using namespace std ;
    const int MAXN = 100005 ; 
    inline int read(){
    	int s=0 ; char g=getchar() ; while( g>'9'||g<'0' )g=getchar() ; 
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ; 
    }
    int N , M , l[MAXN] , r[MAXN] ;
    pair<int,int>calc( pair<int,int> a ){
    	return make_pair( a.first-a.second , a.first+a.second ) ;
    }
    inline bool check( int lim ){
    	int xl = -2*N, yl = -2*N , xr = 2*N , yr = 2*N ; 
    	for( int i = 1 ; i <= M ; ++i ){
    		if( r[i]-l[i] <= lim )continue ;
    		pair<int,int>L,R ;//左下角和右上角的点 
    		L = calc( make_pair( l[i]-lim,r[i] ) ) ; 
    		R = calc( make_pair( l[i]+lim,r[i] ) ) ;
    		xl = max( xl , L.first ) , xr = min( xr , R.first ) ;
    		yl = max( yl , L.second ) , yr = min( yr , R.second ) ; 
    		if( xl > xr || yl > yr )return false ; 
    	}
    	return true ;
    }
    void  dx( int l , int r ){
    	int ans = N ; 
    	while( l <= r ){
    		int mid = (l+r)>>1 ; 
    		if( check(mid) )ans = mid , r = mid-1;
    		else l = mid+1 ; 
    	}
    	cout<<ans ;
    } 
    int main(){
    	freopen("b.in","r",stdin) ;
    	freopen("b.out","w",stdout ) ;
    	N = read() , M = read() ; 
    	for( int i = 1 ; i <= M ; ++i )l[i] = read() , r[i] = read() ; 
    	if( M <= 1 ){puts("1");return 0;}
    	dx( 1 , N ) ; 
    	return 0 ; 
    } 
    

    c

    IOI2015D2T2弱化版

    给出 N 个整数表示 0-N-1 的排列。

    再给出 2N 轮小B的操作。

    我们考虑到,如果固定了游戏的次数 , 即小B先玩 k 轮再让小 A 连续玩K轮的结果是一样的。

    这样考虑问题就变简单了。假设已知 k 轮游戏后可以结束,那么问题就变成了先让小B按指定顺序游戏, 小A 在连续游戏K轮使得变化后的序列转化为一个升序序列 。

    这有用到一个经典模型 ,把每个位置看做一个点 , 向 val[i] 连边 , 最小交换次数就是 N-环的数量 。

    考虑到小A可以在一回合啥都不做,所以答案具有单调性,二分即可。

    代码

    #include<bits/stdc++.h>
    using namespace std ; 
    const int MAXN = 200005 ;
    inline int read(){
    	int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ; 
    	while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ;return s; 
    }
    int N , x[MAXN*2] , y[MAXN*2] , a[MAXN] , b[MAXN] ;
    bool vis[MAXN] ;
    inline bool check( int lim ){
    	for( register int i = 0 ; i < N ; ++i )b[i] = a[i] , vis[i] = false ;
    	for( register int i = 0 ; i < lim ; ++i )
    	   swap( b[x[i]] , b[y[i]] ) ; 
    	int tot = 0 ; 
    	for( int i = 0 ; i < N ; ++i ){
    		if( vis[i] )continue ; 
    		vis[i] = true ; 
    		int now = i ; 
    		while( !vis[b[now]] ){
    			tot++ ; 
    			now = b[now] ;
    			vis[now] = true ;  
    		}
    	} 
    	return ( tot <= lim )?1:0 ;
    }
    void  dx( int l , int r ){ 
    	int ans = 2*N ; 
    	while( l <= r ){
    		int mid = (l+r)>>1 ; 
    		if( check(mid) )ans = mid , r = mid - 1 ;
    		else l = mid + 1 ;
    	}
    	cout<<ans ; 
    }
    int main(){
    	freopen("c.in","r",stdin) ; 
    	freopen("c.out","w",stdout) ;
    	N = read() ; int flag = 1 ;
    	for( int i = 0 ; i < N ; ++i ){
    		a[i] = read() ; if(a[i]!=i)flag = 0 ;
    	} 
    	for( int i = 0 ; i < 2*N ; ++i )x[i] = read() , y[i] = read() ; 
    	if( flag ){cout<<"0" ; return 0 ;}
    	dx( 1 , 2*N ) ; //这个二分会模糊1,0边界问题,特判下
    	return 0 ;
    }
    

    总结

    状态还可以。不过考场忘记exgcd的最小整数解写法废了不少时间。

    T1 时间浪费在寻找时间复杂度的问题上(事实证明我最后T了一个点。。),还有exgcd。。。

    T2的验证很及时,不然60分就没有了。今天的开题顺序还可以斟酌一下。这种经典的模型如果没见过,实际上也不需要花太多时间在一个暴力程序上面卡常,还不如好好检查。

    T3 没有打算开正解很吃亏,实际上ssw02要是一来就开正解的话很容易想到交换问题 ,有此想到二分也会很快 。 但是对于经典的交换K次转化为升序问题ssw02确实没写过,这个要好好总结一下,很妙。

  • 相关阅读:
    openldap
    Java实现 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…
    Java实现 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P1567 统计天数
    Java实现 洛谷 P1567 统计天数
  • 原文地址:https://www.cnblogs.com/ssw02/p/11799831.html
Copyright © 2011-2022 走看看