致出题人:此篇blog中所提到的题目如果侵犯了您的版权,请与me联系,me将及时删除。
说在前面
继几天的低迷之后,终于算是考出了一次比较正常的成绩。
第三题的数据有锅啊啊!本来可以AC第三题的,被数据坑到只有30分
过了一会,出题方发现貌似自己都过不了,于是改了一部分数据。
然而,然而,剩下的数据仍然有问题…因为我的check和std有区别,就是没法过= =
Emmmmm…今天考试的感觉比较不错,先浏览了一下所有题感觉看不出来哪道题很水,但是觉得T3应该不简单,于是决定顺着写。很快的切掉了第一道题,然后在第二道题卡住了。me本来想转换成图论模型写的,然而发现不可做,于是跳过。第三题,me在想的时候,突然灵光一现发现一个性质,感性理解了一下发现找不出反例,于是就开始敲,敲出来可以过很多很多数据,感觉很稳对吧。然而我觉得不能这么玄学,必须想出一个至少能让自己信服的证明!于是花了很久很久很久…终于自己证明了出来。回头敲了个T2的暴力就交卷了。
今天的考试和以往两次感觉就很不一样,能沉浸到题目中,潜心下来分析题目性质,写起来也没有那么困难。明天的考试要注意感受一下思考题目时的方向,在把握题目的大致方向上多花一点时间。
Emmmmmm…..噢噢对,还要注意心态。
大概是这么一些吧=w=
离NOIP只有4天啦,加油噢!
T1
数据范围1e9
解法
式子出来了就已经是简单题啦=w=
对于题目所描述的操作,我们可以写成这个样子:
化简:
又因为上面的式子结果为T,又可以写成:
那么我们只需要判断是否存在这样的一些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() ;
}