2017.11.7机房小测-K进制分解应用/模型转换-矩形并/题目性质-IOI2015原题简化

致出题人:此篇blog中所提到的题目如果侵犯了您的版权,请与me联系,me将及时删除。



说在前面

继几天的低迷之后,终于算是考出了一次比较正常的成绩。
第三题的数据有锅啊啊!本来可以AC第三题的,被数据坑到只有30分
过了一会,出题方发现貌似自己都过不了,于是改了一部分数据。
然而,然而,剩下的数据仍然有问题…因为我的check和std有区别,就是没法过= =

Emmmmm…今天考试的感觉比较不错,先浏览了一下所有题感觉看不出来哪道题很水,但是觉得T3应该不简单,于是决定顺着写。很快的切掉了第一道题,然后在第二道题卡住了。me本来想转换成图论模型写的,然而发现不可做,于是跳过。第三题,me在想的时候,突然灵光一现发现一个性质,感性理解了一下发现找不出反例,于是就开始敲,敲出来可以过很多很多数据,感觉很稳对吧。然而我觉得不能这么玄学,必须想出一个至少能让自己信服的证明!于是花了很久很久很久…终于自己证明了出来。回头敲了个T2的暴力就交卷了。

今天的考试和以往两次感觉就很不一样,能沉浸到题目中,潜心下来分析题目性质,写起来也没有那么困难。明天的考试要注意感受一下思考题目时的方向,在把握题目的大致方向上多花一点时间。
Emmmmmm…..噢噢对,还要注意心态。

大概是这么一些吧=w=
离NOIP只有4天啦,加油噢!


T1

这里写图片描述
数据范围1e9

解法

式子出来了就已经是简单题啦=w=
对于题目所描述的操作,我们可以写成这个样子:

( (( ( (S+t1a)b)+t2a)b)+tma)b+tm+1a

化简:
bmS+Ka   (K=t1bm+t2bm1+...+tmb1+tm+1b0

又因为上面的式子结果为T,又可以写成:
TbmS   ( mod a)

那么我们只需要判断是否存在这样的一些m满足上述条件(这里可以用扩展欧几里得)。如果可以,那么我们可以直接求出K,并将K进行b进制分解从而得到某一种可行方案的步数,最小的步数就是答案。

下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std ;

int cnt ;
long long S , T , a , b , mi[35] = {1} , ans = 100000000000LL;

LL exgcd( LL a , LL b , LL &x , LL &y ){
    if( !b ) {
        x = 1 , y = 0 ;
        return a ;
    } else {
        LL xx , yy , d ;
        d = exgcd( b , a%b , xx , yy ) ;
        x = yy ; y = xx - a / b * yy ;
        return d ;
    }
}

long long div( LL x , int lim ){
    long long rt = 0 ;
    for( int i = lim ; i >= 0 ; i -- ){
        if( mi[i] > x ) continue ;
        else{
            rt += ( x / mi[i] ) ;
            x %= mi[i] ;
        }
    }
    return rt ;
}

void solve(){
    //x*S + y*a == T 
    long long x , y , d = exgcd( S , a , x , y ) , delta = S / d ;
    y *= T / d ;
    y = ( y %delta + delta )%delta ;
    x = ( T - y * a ) / S ;

    if( x < 0 ) {
        printf( "-1" ) ;
    } else {
        //T = k*S + q*a , k = b^? , q = t1*b^m + t2*b^m-1 + ... + tm+1*1 ;
        //T - k*S === 0 ( mod a )
        for( int i = cnt ; i >= 0 ; i -- ) {
            if( mi[i] > x ) continue ;
            if( ( T - mi[i]*S ) % a != 0 ) continue ;
            LL q = ( T - mi[i]*S ) / a ;
            ans = min( ans , i + div( q , i ) ) ;
        }
        printf( "%lld" , ans ) ;
    }
}

int main(){
    scanf( "%lld%lld%lld%lld" , &S , &T , &a , &b ) ;
    for( cnt = 1 ; mi[cnt-1] * b * S <= T ; cnt ++ )
        mi[cnt] = mi[cnt-1] * b ;
    cnt -- ;
    solve() ;
}


T2

这里写图片描述
这里写图片描述

解法

二分答案。下面解释如何check
我们只用考虑那些长度大于了mid的点对。对于一个点对(x,y),我们可以将其看作坐标系上的点,那么对于这个mid,可以当作一个距离限制,表示与(x,y)这个点曼哈顿距离在mid以内的整点(u,v)都是符合要求的(这些整点集合是一个正方形,在这些

下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M ;
struct Data{
    int x , y , len ;
}d[100005] ;

int topp , sta[100005] ;
bool check( int mid ){
    topp = 0 ;
    for( int i = 1 ; i <= M ; i ++ )
        if( d[i].len > mid ) sta[++topp] = i ;

    int maxn = -0x3f3f3f3f , minn = 0x3f3f3f3f ;
    for( int i = 1 ; i <= topp ; i ++ ){
        int minus = d[ sta[i] ].x - d[ sta[i] ].y ;
        maxn = max( maxn , minus ) ;
        minn = min( minn , minus ) ;
    }
    if( maxn - minn > 2 * mid ) return false ;

    maxn = -0x3f3f3f3f , minn = 0x3f3f3f3f ;
    for( int i = 1 ; i <= topp ; i ++ ){
        int add = d[ sta[i] ].x + d[ sta[i] ].y ;
        maxn = max( maxn , add ) ;
        minn = min( minn , add ) ;
    }
    if( maxn - minn > 2 * mid ) return false ;

    return true ;
}

void solve(){
    int lf = 0 , rg = N , ans ;
    while( lf <= rg ) {
        int mid = ( lf + rg ) >> 1 ;
        if( check( mid ) ) {
            ans = mid ;
            rg = mid - 1 ;
        } else lf = mid + 1 ;
    }
    printf( "%d" , ans ) ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 ; i <= M ; i ++ ) {
        scanf( "%d%d" , &d[i].x , &d[i].y ) ;
        if( d[i].x > d[i].y )
            swap( d[i].x , d[i].y ) ;
        d[i].len = d[i].y - d[i].x ;
    }
    solve() ;
} 


T3

这里写图片描述
这里写图片描述

解法

首先这个题是具有二分性质的。如果在第i步就已经可以换成升序序列,在第i+1步时,A的操作只需要保持和B的操作相同(相当于抵消掉了),结果仍然是升序的。因此该答案有二分性

那么这里还需要用到一个性质(就这么简单我想了接近一个小时,太蠢了…)。这里我就直接上结论吧。
对于A,B两人在序列中进行的交换操作,我们可以颠倒他们的顺序而使结果不变。(A是己方,己方可以改变自己的策略)
简单理解:
假设A交换的是(x1,x2),B交换的是(y1,y2)
1.如果x1≠x2且y1≠y2 那么直接交换顺序不影响结果
2.如果x1=x2且y1=y2 那么第一个人的操作会被第二个人的抵消,因此交换也不影响结果
3.如果x1=x2或者y1=y2
      我们假设A交换了a,b,而B交换了b,c。
      如果B先操作,那么后操作的A只需要交换a,c,结果和原操作顺序后得到的结果一样。

由此,如果存在一种可行方案,那么我把所有的B的操作提前,仍然应当存在一种可行方案。又因为符合二份性,所以直接O(n)check就可以了。
在寻找一个可行方案的时候,直接for过去即可(如果i位置不是i,那么就把序列里的0和0位置上的数字交换即可,这样一定是最优的,不理解可以自己模拟)

下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , N2 , ori[200005] , arc[200005] , x[400005] , y[400005] ;
int tmp[200005] , tarc[200005] ;

bool check( int mid ){
    memcpy( tmp , ori , sizeof( tmp ) ) ;
    memcpy( tarc , arc , sizeof( tarc ) ) ;

    for( int i = 1 ; i <= mid ; i ++ ){
        swap( tarc[ tmp[x[i]] ] , tarc[ tmp[y[i]] ] ) ;
        swap( tmp[ x[i] ] , tmp[ y[i] ] ) ;
    }
    int cnt = 0 ;
    for( int i = 1 ; i <= N ; i ++ ){
        if( tmp[i] != i ){
            int pi = tarc[i] , pk = tarc[ tmp[i] ] ;
            swap( tarc[ tmp[i] ] , tarc[i] ) ;
            swap( tmp[pi] , tmp[pk] ) ;
            cnt ++ ;
            if( cnt > mid ) return false ;
        }
    }
    return true ;
}

void solve(){
    int lf = 0 , rg = N2 , ans ;
    while( lf <= rg ) {
        int mid = ( lf + rg ) >> 1 ;
        if( check( mid ) ) {
            ans = mid ;
            rg = mid - 1 ;
        } else lf = mid + 1 ;
    }
    printf( "%d" , ans ) ;
}

int main(){
    scanf( "%d" , &N ) ; N2 = 2 * N ;
    for( int i = 1 ; i <= N ; i ++ )
        scanf( "%d" , &ori[i] ) , arc[ ++ori[i] ] = i ;
    bool fix = -1 ;
    for( int i = 1 ; i <= N2 ; i ++ ){
        scanf( "%d%d" , &x[i] , &y[i] ) ;
        if( x[i] == 0 || y[i] == 0 ) fix = 1 ;
        if( x[i] == N || y[i] == N ) fix = 0 ;
    }
    if( !fix ){
        for( int i = 1 ; i <= N2 ; i ++ )
            x[i] -= 1 , y[i] -= 1 ;
    }
    solve() ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值