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确实没写过,这个要好好总结一下,很妙。

  • 相关阅读:
    【BZOJ3144】切糕(HNOI2013)-最小割
    【BZOJ1934】善意的投票(SHOI2007)-最小割
    【BZOJ2125】最短路-圆方树+倍增LCA
    【BZOJ4868】期末考试(六省联考2017)-三分
    【BZOJ1951】古代猪文(SDOI2010)-数论大集合
    【BZOJ2257】瓶子和燃料(JSOI2009)-裴蜀定理+排序
    【BZOJ1485】有趣的数列(HNOI2009)-卡特兰数+线性筛
    【51Nod1952】栈-单调栈+单调队列
    【BZOJ4517】排列计数(SDOI2016)-组合数学:错排
    【BZOJ2111】排列计数(ZJOI2010)-DP+Lucas定理
  • 原文地址:https://www.cnblogs.com/ssw02/p/11799831.html
Copyright © 2011-2022 走看看