2022寒假训练week1

Day 1

AcWing 2041. 干草堆

简单的前缀和,加上一点点读入优化

int main()
{
    n = read() , k = read();
    for( int i = 1 , l , r ; i <= k ; i ++ )
    {
        l = read() , r = read();
        a[ l ] ++ , a[ r + 1 ] --;
    }
    for( int i = 1 ; i <= n ; i ++ ) a[i] += a[ i - 1 ];
    sort( a + 1 , a + 1 + n );
    cout << a[ n / 2 + 1  ] << endl;
    return 0;
}

AcWing 2058. 笨拙的手指

枚举与 a b 只有一位不相同的数

并找出两个集合的交集

这里有 2 或 3 进制转换十进制用到了很常用的秦九韶算法

#include <bits/stdc++.h>
using namespace std;

string a , b;
set< int >s;

int get( string d , int k )
{
    int res = 0;
    for( auto c : d )
        res = res * k + c - '0';
    return res;
}

int main()
{
    cin >> a >> b;
    for( auto & c : a )
    {
        c ^= 1;
        s.insert( get( a , 2 ) );
        c ^= 1;
    }

    for( auto & c : b )
    {
        auto t = c;
        for( int i = 0 ; i < 3 ; i ++ )
        {
            if( i + '0' != t )
            {
                c = i + '0';
                int x = get( b , 3 );
                if( s.count(x) )
                {
                    cout << x << endl;
                    return 0;
                }
            }
        }
        c = t;
    }
}

Day 2

AcWing 1987. 粉刷栅栏

本质上就是一个差分,然后因为数据范围比较大,自然需要hash,同时因为我们需要顺序遍历,就要用到map,这里还用了一个c++17的新特征

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int n ;
map< int, int > b;

int read()
{
    int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

int readop()
{
    char ch = getchar();
    while( ch != 'L' && ch != 'R' ) ch = getchar();
    return ( ch == 'R' ) ? 1 : 0 ;
}

int main()
{
    n = read(); 
    for( int x = 0 , y , op ; n ; n -- )
    {
        y = read() , op = readop();
        if( op ) b[x] ++ , b[ x + y ] -- , x += y;
        else b[ x - y ] ++ , b[x] -- , x -= y;
    }
    int res = 0 , sum = 0 , last = 0;
    for( auto&[ x , v ] : b )
    {
        if( sum >= 2 ) res += x - last;
        sum += v , last = x;
    }
    cout << res << endl;
}

AcWing 1978. 奶牛过马路

先说\(o(n^2)\)的做法,就是用快速判据,一条线的一个端点更小、另一个端点更大则两条直线交叉。这个做法会\(TLE\)

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int n , a[N] , b[N] , res ;
bool f[N];

inline int read()
{
    int x = 0 , f = 1 , ch = getchar();
    while( ( ch < '0' || ch > '9' ) && ch != '-' ) ch = getchar();
    if( ch == '-' ) f = -1 , ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch -'0' , ch  = getchar();
    return x * f;
}

int main()
{
    n = read();
    for( int i = 1 ; i <= n ; i ++ ) a[i] = read() , b[i] = read();
    memset( f , 1 , sizeof(f) );
    for( int i = 1 ; i <= n ; i ++ )
    {
        for( int j = 1  ; j <= n && f[i] ; j ++ )
        {
            if( i == j ) continue;
            if( ( a[i] <= a[j] && b[i] >= b[j] ) || ( a[i] >= a[j] && b[i] <= b[j] ) ) f[i] = f[j] = 0;
        }
            
    }
    for( int i = 1 ; i <= n ; i ++ ) res += f[i];
    cout << res << endl;
}

再说\(O(nlog_n)\)的做法,将所有的点按照\(a_i\)排序

依次遍历,如果\(b\)的前缀最大值比当前小,后缀最小值比当前大,则这条线不与其他线相交

#include <bits/stdc++.h>
#define F first
#define S second
using namespace std;

const int N = 1e5 + 5;
int n , res , maxS[N] , minS[N];
pair< int , int > node[N];

inline int read()
{
    int x = 0 , f = 1 , ch = getchar();
    while( ( ch < '0' || ch > '9' ) && ch != '-' ) ch = getchar();
    if( ch == '-' ) f = -1 , ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch -'0' , ch  = getchar();
    return x * f;
}

int main()
{
    n = read();
    for( int i = 1 ; i <= n ; i ++ ) node[i].F = read() , node[i].S = read();
    sort( node + 1 , node + 1 + n );
    maxS[0] = - 0x7f7f7f7f ; for( int i = 1 ; i <= n ; i ++ ) maxS[i] = max( maxS[ i - 1 ] , node[i].S );
    minS[n+1] = 0x7f7f7f7f ; for( int i = n ; i >= 1 ; i -- ) minS[i] = min( minS[ i + 1 ] , node[i].S );
    for( int i = 1 ; i <= n ; i ++ )
        if( maxS[ i - 1 ] < node[i].S && minS[ i + 1 ] > node[i].S ) res ++;
    cout << res << endl;
    return 0;
}

Day 3

AcWing 1969. 品种邻近

首先比较裸的写法就是直接\(n^2\)的暴力,数据范围是\(5e4\)显然不可以

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int n , k , id[N] , res = -1;

int read()
{
    int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

int main()
{
    n = read() , k = read();
    for( int i = 1 ; i <= n ; i ++ ) id[i] = read();
    for( int i = 1 ; i <= n ; i ++ )
    {
        if( id[i] <= res ) continue;
        for( int j = max( 1 , i - k ) ; j <= min( n , i + k ) ; j ++ )
        {
            if( id[i] != id[j] || i == j ) continue;
            res = id[i];
            break;
        }
    }
    cout << res << endl;
}

last[]记录每个id最后一次出现在哪里,如果和当前的距离小于等于k,就是拥挤奶牛对

#include <bits/stdc++.h>
using namespace std;

const int M = 5e4 + 5 , N = 1e6 + 5;
int n , k , last[N] , res = -1;

int read()
{
    int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

int main()
{
    n = read() , k = read();
    for( int i = 1 , idn; i <= n ; i ++ )
    {
        idn = read();
        if( last[idn] && i - last[idn] <= k ) res = max( res , idn );
        last[idn] = i;
    }
    cout << res << endl;
}

CF#765 div.2

A

题意是给定n个数,求一个数与这n个数分别异或后在二进制下1的和最少

统计所有数二进制下每一位的1的个数,如果数量大于等一\(\frac{n}{2}\),则res的这一位就是1,反之是0

#include <bits/stdc++.h>
using namespace std;

const int N = 105;
int n , k , res , a[N];

int read()
{
    int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0', ch = getchar();
    return x;
}

void slove()
{
    n = read() , k = read() , res = 0;
    for( int i = 1 ; i <= n ; i ++ ) a[i] = read();
    for( int i = 0 , cnt ; i < k ; i ++ )
    {
        cnt = 0;
        for( int j = 1 ; j <= n ; j ++ )
            if( ( a[j] >> i ) & 1 ) cnt ++;
        if( cnt * 2 >= n ) res += ( 1 << i );
    }
    printf("%d\n" , res );
}

int main()
{
    int t = read();
    while( t -- ) slove();
}

B

对于给定长度为\(n\)的序列,要满足两个不同子序列长度相同,且至少有一个对应位置上相等。

首先把每个数的位置都存下来,任意枚举两个数,前一个数的前缀和后一个数的后缀长度相加就是这个对于位置相同的最长子序列,易知当两个数相邻时,子序列最长

#include <bits/stdc++.h>
using namespace std;

const int N = 15e4+5;
int n , k , res , maxn;
vector< int > a[N];

int read()
{
    int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0', ch = getchar();
    return x;
}

void slove()
{
    n = read() , res = -1;
    for( int i = 0 ; i <= maxn ; i ++ ) a[i].clear();
    maxn = 0;
    for( int i = 1 , x ; i <= n ; i ++ ) x = read() , a[x].push_back( i ) , maxn = max( maxn , x );
    for( int l = 0 ; l <= maxn ; l ++ )
    {
        if( a[l].size() == n )
        {
            cout << n - 1 << endl;
            return ;
        }
        for( int i = 0 , j = 1 ; j < a[l].size() ; i ++ , j ++ )
            res = max( res , a[l][i] + n - a[l][j] );
    }
    cout << res << endl;
    return ;
}

int main()
{
    int t = read();
    while( t -- ) slove();
}

Day4

牛客小白月赛43

A

m次询问,每次给一个n询问1n之间有几个满意的数字

满意的数字的定义是,每个数所有的因子从小到大排序后,最后一个因子能被最中间的因子整除。

#include <bits/stdc++.h>
#define F first
#define S second
using namespace std;

const int N = 1e4 + 5;
int res[N];

inline int read()
{
    int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch -'0' , ch  = getchar();
    return x;
}

bool slove( int t )
{
    vector< int > v; v.push_back(0);
    for( int i = 1 ; i <= t ; i ++ )
        if( t % i == 0 ) v.push_back(i);
    int m = v.size()-1;
    if( v[m] % v[ (m+1)/2 ] == 0  ) return 1;
    else return 0;
}

int main()
{
    for( int i = 1 ; i <= 1000 ; i ++ )
        res[i] = res[i-1] + slove( i );
    int t = read();
    for( int i = 1 , x ; i <= t ; i ++ )
        x = read() , printf( "%d\n" , res[x] );
}

赛后发现所有的因子中每一个数最大的因子都是本身,所以必然可以被所有的因子整除,比赛中没发现是因为一开始读错题目了

有了这个结论就有了以下的代码。

t = int( input() )
for i in range(0,t):
    print(input())

B

ab等于t时,则不用操作,直接输出0

t为奇数时则一定无解,因为只要操作就只能得到偶数

其他情况sum=a+b要满足大于等于t/2,所以先不断翻倍,当大于等于一半时,先凑出一半,在进行翻倍即可

#include <bits/stdc++.h>
#define ll long long
using namespace std;

ll a , b , t , res;

void slove()
{
    cin >> a >> b >> t;
    if( a == t || b == t ) printf("0\n");
    else if( t % 2 ) printf("-1\n");
    else if( a+b >= t/2 ) printf("1\n");
    else
    {
        ll res = 0;
        a += b;
        while( a < t / 2 ) a *= 2 , res ++ ;
        cout << res+1 << endl;
    }
}

int main()
{
    int k;
    cin >> k;
    while( k -- ) slove();
    return 0;
}

Day5

AcWing 1960. 闪烁

因为最多只有\(2^{16}=65536\)种情况,每一种情况的下一种情况是固定的,而\(B\)可以取到\(10^{15}\)所以\(B\)较大时一定会出现环的情况

对于每一种情况我们用一个二进制数来表示,然后对于每个灯泡与左边的灯泡异或就是下一种情况。

p[]记录每种状态第一次出现是在第几步,第二次出现与第一次相减就是环的大小。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = ( 1 << 16 ) + 5;
ll n , m ;
int p[N];

int update( int state)
{
    int res = 0;
    for( int i = 0 , j , x , y; i < n ; i ++)
    {
        j = ( i - 1 + n ) % n ;
        x = state >> i & 1 , y = state >> j & 1 ;
        res |= ( x ^ y ) << i ;
    }
    return res;
}

void print( int state )
{
    for( int i = 0 ; i < n ; i ++ )
        cout << ( state >> i & 1 ) << endl;
}

int main()
{
    cin >> n >> m;
    int state = 0;
    for( int i = 0 , x ; i < n ; i ++ )
    {
        cin >> x;
        state |= x << i;
    }
    memset(  p , -1 , sizeof(p) );
    p[state] = 0;
    for( int i = 1 ; ; i ++ )
    {
        state = update(state);
        if( i == m ) print(state) , exit(0);
        else if( p[state] == -1 ) p[state] = 0;
        else
        {
            int len = i - p[state] , r = ( m - i ) % len ;
            while( r -- ) state = update(state);
            print(state);
            exit(0);
        }
    }
    return 0;
}

Day 6

AtCoder Beginner Contest 235

A

签到题

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int a , b = 0;
    cin >> a;
    while(a) b += a % 10 , a /= 10;
    cout << b * 111 << endl;
    return 0;
}

B

签到题

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n , res ;
    cin >> n >> res ;
    for( int i = 2 , x ; i <= n ; i ++ )
    {
        cin >> x;
        if( x > res ) res = x;
        else break;
    }
    cout << res << endl;
}

C

题目是给我一个序列A问数xk次出现的位置

正解应该是hash一下,然后记录每个数出现的位置,这里可以直接用mapvector解决,但是不知道为什么unordered_map会莫名其妙RE

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+5;
int n , q , cnt;
map< int , vector<int> > h;

inline int read()
{
    int x = 0 , ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x;
}

int main()
{
    n = read() , q = read();
    for( int i = 1 , x ; i <= n ; i ++ )
        x = read() , h[x].push_back(i);
    for( int i = 1 , x , y ; i <= q ; i ++ )
    {
        x = read() , y = read();
        if( h.find(x) == h.end() ) printf("-1\n");
        else
        {
            if( y > h[x].size() ) printf("-1\n");
            else printf( "%d\n" , h[x][y-1] );
        }
    }
    return 0;
}

D

题目意思是,给定an ,问从1开始,每次可以乘a、也可以吧各位放在最高位上(123456可以转化成612345)问最少多少次可以变成a

一开始想过双向bfs和id-dfs,最后估计发现bfs和记忆法就可以完成

然而这道题卡long longre了好几遍都没想起来

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 2e6+1;
int a , n;
ll dis[N+5];
bitset<N+5> vis;
queue<ll>q;

int main()
{
    cin >> a >> n;
    q.push(1) , vis[1] = 1;
    ll x , y;
    string s , t;
    while(q.size())
    {
        x = q.front() , q.pop();
        if( x == n )
        {
            cout << dis[n] << "\n";
            return 0;
        }

        if( x * a < N && !vis[x*a] )
            vis[x*a] = 1 , q.push( x*a ) , dis[x*a] = dis[x] + 1;

        if( (x%10) && (x>10) )
        {
            t = to_string(x);
            s = t.back() + t.substr( 0 , t.size() - 1 );
            y = stoi(s);
            if( y < N && !vis[y] )
                vis[y] = 1 , q.push(y) , dis[y] = dis[x]+1;
        }
    }
    cout << "-1\n";
    return 0;
}

Day 7

AcWing 2060. 奶牛选美

数据范围很小,可以暴力求出两块斑点内的所有点然后比较,复杂度为 O(nm)

#include <bits/stdc++.h>
using namespace std;

const int N = 55;
const int dx[] = {0,0,1,-1} , dy[]={1,-1,0,0};
int n , m;
char st[N][N];
vector< pair< int ,int> >notes[2];
void dfs( int x , int y , vector< pair<int,int> > &ns )
{
    ns.emplace_back(x,y) , st[x][y] = '.';
    for( int i = 0 , fx , fy ; i < 4 ; i ++ )
    {
        fx = x + dx[i] , fy = y + dy[i];
        if( fx < 1 || fy < 1 || fx > n || fy > m || st[fx][fy] == '.' ) continue;
        dfs( fx , fy , ns );
    }
}

inline int getdis( const pair<int,int> & a , const pair<int,int> & b)
{
    return abs( a.first - b.first ) + abs( a.second - b.second );
}

int main()
{
    cin >> n >> m;
    for( int i = 1 ; i <= n ; i ++ )
        cin >> (st[i]+1);
    for( int i = 1 , k = 0 ; i <= n ; i ++ )
    {
        for( int j = 1 ; j <= m ; j ++ )
        {
            if( st[i][j] == 'X' )
                dfs( i , j , notes[k] ) , k ++;
        }
    }

    int res = N*N;
    for( auto i : notes[0] )
        for( auto j : notes[1] )
            res = min( res , getdis( i , j ) );
    cout << res - 1 << endl;
    return 0;
}

AcWing 2019. 拖拉机

虽然y总的正解是双端队列bfs,但是我用spfa也能水过去

有草堆的点是1,其他点是0

#include <bits/stdc++.h>
using namespace std;

const int N = 1005;
const int dx[] = {0 , 0 , 1 , -1 } , dy[] = { 1 , -1 , 0 , 0};
int st[N][N] , dis[N][N], n , sx , sy;
bool vis[N][N];

void spfa()
{
    queue< pair<int,int> >q;
    q.push({sx,sy}) , memset(dis , 0x3f , sizeof(dis));
    dis[sx][sy] = 0 , vis[sx][sy] = 1;
    while(q.size())
    {
        pair<int,int> t = q.front() ; q.pop() , vis[t.first][t.second] = 0;
        for( int i = 0 , a , b ; i < 4 ; i ++ )
        {
            a = t.first + dx[i] , b =t.second + dy[i];
            if( a < 0 || a > N-1 || b < 0 || b > N-1 ) continue;
            if(dis[a][b] <= dis[t.first][t.second] + st[t.first][t.second] ) continue;
            dis[a][b] = dis[t.first][t.second] + st[t.first][t.second];
            if(!vis[a][b])
                q.push({a,b}) , vis[a][b] = 1;
        }
    }
}
int main()
{
    cin >> n >> sx >> sy;
    for( int x , y ; n ; n -- )
    {
        cin >> x >> y;
        st[x][y] = 1;
    }
    spfa();
    cout << dis[0][0] << endl;
    return 0;
}
posted @ 2022-01-16 20:38  PHarr  阅读(41)  评论(0编辑  收藏  举报