10月19日训练记录 CF GYM 101911

A题

题意:喝n杯咖啡,但两杯咖啡的间隔时间要大于d,问最少需要几天才能喝上n杯咖啡

思路:贪心喝咖啡,每一杯都选时间符合要求的,如果找不到时间符合要求的就加一天

代码:数据结构减智力,可以用set简单维护。。。

#include<bits/stdc++.h>
using  namespace std;
#define lson rt<<1
#define rson rt<<1|1
const int maxn = 200010;
const int inf  = 0x3f3f3f3f;
struct node
{
    int val,idx;
    friend bool operator<( const node&a , const node&b )
    {
        return a.val<b.val;
    }
}a[maxn]; int ans[maxn];
int Min( int a , int b ){ return a<b?a:b; };
int minx[maxn<<2],idxx[maxn<<2],b[maxn];
void push_up( int rt )
{
    if ( minx[lson]<=minx[rson] )
    {
        minx[rt] = minx[lson]; idxx[rt] = idxx[lson];
    }
    else
    {
        minx[rt] = minx[rson]; idxx[rt] = idxx[rson];
    }
}
void build( int l , int r , int rt )
{
    if ( l==r )
    {
        b[l] = -inf; minx[rt] = -inf; idxx[rt] = l; return;
    }
    int mid = ( l+r )>>1;
    build( l , mid , lson );
    build( mid+1 , r , rson );
    push_up( rt );
}
void update( int p , int x , int l , int r , int rt )
{
    if ( l==r )
    {
        b[l] = x; minx[rt] = x; return;
    }
    int mid = (l+r)>>1;
    if ( p<=mid ) update( p , x , l , mid , lson );
    else update( p , x , mid+1 , r , rson );
    push_up( rt );
}
int  query( int L , int R , int l , int r , int rt )
{
    if ( L<=l&&R>=r ) return idxx[rt];
    int mid =(l+r)>>1,posx = -1,pl = -1,pr = -1;
    if ( L<=mid ) pl = query( L , R , l , mid , lson );
    if ( R> mid ) pr = query( L , R , mid+1 , r , rson );
    if ( pl!=-1&&(posx==-1||b[pl]<b[posx]) ) posx = pl;
    if ( pr!=-1&&(posx==-1||b[pr]<b[posx]) ) posx = pr;
    return posx;
}
int main()
{
    for ( int n,m,d ; scanf( "%d%d%d" , &n , &m , &d )==3 ; )
    {
        for ( int i=1 ; i<=n ; i++ ) scanf( "%d" , &a[i].val ),a[i].idx = i; sort( a+1 , a+n+1 ); build( 1 , n , 1 );
        int sum = 1; update( 1 , a[1].val , 1 , n , 1 );  ans[a[1].idx] = 1;
        for ( int i=2 ; i<=n ; i++ )
        {
            int t = query( 1 , sum , 1 , n , 1 );
            if ( a[i].val>b[t]+d ) update( t , a[i].val , 1 , n , 1 ),ans[a[i].idx] = t;
            else sum++,update( sum , a[i].val , 1 , n , 1 ),ans[a[i].idx] = sum;
        }
        printf( "%d\n" , sum );
        for ( int i=1 ; i<=n ; i++ )
        {
            if ( i!=1 ) printf( " " );
            printf( "%d" , ans[i] );
        }
        printf( "\n" );
    }
    return 0;
}

B题

题意:n段不相交的区间,在这些区间内移动不需要花费(进区间前必须还有剩余),区间外移动需要每单位一点,求h点最多可以向前走几个单位

思路:起点肯定是某个区间的起点,但枚举起点显然会超时,但显然从当前起点向前可以穿过的区间数和其花费是单调递增,所以处理出前缀和后枚举起点二分终点即可获得答案

代码:

#include<bits/stdc++.h>
using  namespace std;
const int maxn = 200010;
int  Max( int a , int b ){ return a>b?a:b; }
int  a[maxn],b[maxn];
struct node
{
    int l,r;
    friend bool operator<( const node&a , const node&b )
    {
        return a.l<b.l;
    }
}c[maxn];

int main()
{
    for ( int n,h ; scanf( "%d%d" , &n , &h )==2 ; )
    {
        for ( int i=1 ; i<=n ; i++ )
            scanf ( "%d%d" , &c[i].l , &c[i].r );
        sort( c+1 , c+n+1 );
        a[0] = 0; a[n] = 2000000010; b[0] = 0;
        for ( int i=1 ; i< n ; i++ )
        {
            a[i] = a[i-1]+c[i+1].l-c[i].r;
            b[i] = b[i-1]+c[i].r-c[i].l;
        }
        b[n] = b[n-1]+c[n].r-c[n].l;
        int ans = 0;
        for ( int i=1 ; i<=n ; i++ )
        {
            int t = lower_bound( a+1 , a+n+1 , a[i-1]+h )-a;
            ans = Max( ans , h+b[t]-b[i-1] );
        }
        printf( "%d\n" , ans );
    }
    return 0;
}

C题

题意:n个物品,若两个物品价值相同则可以合并成一个物品,合成后的物品价值为原来的两倍,问最终合并成一个物品最少需要添加几个物品(添加的物品价值任意),若无法合成一个物品,则输出-1

思路:优先队列维护,每次取队列头两个a和b,若b是a的整数倍并且b/a是2的幂次,则可以添加幂次个物品将这两个物品合并成一个,反之说明无法合成一个物品

代码:

#include<bits/stdc++.h>
using  namespace std;
typedef long long LL;
int main()
{
    LL a[55]; a[0] = 1; for ( int i=1 ; i<55 ; i++ ) a[i] = a[i-1]*2;
    for ( int n ; scanf( "%d" , &n )==1 ; )
    {
        priority_queue< LL , vector<LL> , greater<LL> >Q;
        for ( int i=1 ; i<=n ; i++ )
        {
            LL x; scanf( "%I64d" , &x ); Q.push(x);
        }
        LL ans = 0;
        while ( Q.size()>=2 )
        {
            LL A = Q.top(); Q.pop();
            LL B = Q.top(); Q.pop();
            if ( B%A!=0 )
            {
                ans = -1; break;
            }
            LL t = B/A,l = 0,r = 50;
            while( l<=r )
            {
                LL mid = (l+r)>>1;
                if ( a[mid]>=t ) r = mid-1;
                else l = mid+1;
            }
            if ( a[l]!=t )
            {
                ans = -1; break;
            }
            ans += l; Q.push(B*2);
        }
        printf( "%I64d\n" , ans );
    }
    return 0;
}

D题

题意:n个数需要将每个数拆分成两个整数相乘,但拆分后不能有任何一组相同

思路:排序,将相同的数一起处理,根号枚举因子

代码:

#include<bits/stdc++.h>
using  namespace std;
const int maxn = 200010;
struct node
{
    int val,idx;
    friend bool operator<( const node&a , const node&b )
    {
        return a.val<b.val;
    }
}a[maxn]; int p[maxn],q[maxn];
int main()
{
    for ( int n ; scanf( "%d" , &n )==1 ; )
    {
        for ( int i=1 ; i<=n ; i++ )
            scanf( "%d" , &a[i].val ),a[i].idx = i;
        sort( a+1 , a+n+1 ); bool ok = true;
        for ( int i=1 ; i<=n ; i++ )
        {
            int l=i,r=i,t=a[i].val;
            while ( r+1<=n&&a[r+1].val==a[l].val ) r++;
            for ( int j=1 ; j*j<=t ; j++ )
            {
                if ( t%j==0 )
                {
                    p[a[l].idx] = j;
                    q[a[l].idx] = t/j;
                    l++;
                    if ( l>r )  break;
                    if ( j*j!=t )
                    {
                        p[a[l].idx] = t/j;
                        q[a[l].idx] = j;
                        l++;
                    }
                    if ( l>r ) break;
                }
            }
            if ( l<=r )
            {
                 ok = false; break;
            }
            i = r;
        }
        if ( ok )
        {
            printf( "YES\n" );
            for( int i=1 ; i<=n ; i++ )
                printf( "%d %d\n" , p[i] , q[i] );
        }
        else
            printf( "NO\n" );
    }
    return 0;
}

E题

题意:有n块木板初始有颜色ai...an,刷m次漆,每次刷漆选择一种颜色从当前颜色的最左端刷到当前颜色的最右端,问经过m次刷漆以后最终木板的颜色序列

思路:用set维护各颜色拥有的木板下标,每次涂色直接取set.begin和set.rbegin进行刷漆,此外当一种颜色刷过以后,它所刷过的区间就不再需要遍历,将其左右边界流在set里即可,下次再遍历到这些点直接跳过该区间

代码:

#include<bits/stdc++.h>
using  namespace  std;
const int maxn = 300010;
bool  vis[maxn]; int a[maxn];
set<int>s[maxn];
int main()
{
    for ( int n,m ; scanf( "%d" , &n )==1 ; )
    {
        for ( int i=0 ; i<maxn ; i++ )
        {
            vis[i] = false; s[i].clear();
        }
        for ( int i=1 ; i<=n   ; i++ )
        {
            scanf( "%d" , &a[i] ); s[a[i]].insert(i);
        }
        scanf( "%d" , &m );
        for  ( int i=1 ; i<=m ; i++ )
        {
            int x; scanf( "%d" , &x );
            if ( s[x].size()<2||vis[x] )
            {
                vis[x] = true; continue;
            }
            int l=*(s[x].begin()),r=*(s[x].rbegin());
            for( int j=l+1 ; j<=r-1 ; j++ )
            {
                s[a[j]].erase(j);
                if ( vis[a[j]]&&s[a[j]].size()>=1 )
                {
                    j = *(s[a[j]].begin());
                    s[a[j]].erase(j);
                }
            }
            vis[x] = true;
        }
        for ( int i=0 ; i<maxn ; i++ )
        {
            if ( vis[i]&&s[i].size()>=2 )
            {
                int l = *(s[i].begin()),r = *(s[i].rbegin());
                for ( int j=l ; j<=r ; j++ ) a[j] = i;
            }
        }
        for ( int i=1 ; i<=n ; i++ )
        {
            if ( i!=1 ) printf( " " );
            printf( "%d" , a[i] );
        }
        printf( "\n" );
    }
    return 0;
}

F题

题意:有n次询问每次询问给一个x,且x自动补全六位ABCDEF,询问<x且Abs(A+B+C-D-E-F)<x在此计算式下的数目

思路:排序,离线,暴力操作

代码:

#include<bits/stdc++.h>
using  namespace std;
int Abs( int x ){ return x<0?-x:x; }
const int maxn = 200010;
struct node
{
    int val,idx;
    friend bool operator<( const node&a , const node&b )
    {
        return a.val<b.val;
    }
}a[maxn];
int  ans[maxn],bit[30],m=28;
int  lowbit( int x )
{
    return x&(-x);
}
void add( int x , int val )
{
    for( ; x<=m ; x+=lowbit(x) )
        bit[x] += val;
}
int  sum( int x )
{
    int res = 0;
    for ( ; x>=1 ; x-=lowbit(x) )
        res += bit[x];
    return res;
}
int  cal( int x )
{
    int A = x/1000;
    int B = x%1000;
    return Abs(A/100+A%100/10+A%10-B/100-B%100/10-B%10);
}
int main()
{
    for ( int n ; scanf( "%d" , &n )==1 ; )
    {
        for ( int i=1 ; i<=n ; i++ ) scanf( "%d" , &a[i].val ),a[i].idx = i;
        for ( int i=1 ; i<=m ; i++ ) bit[i] = 0;
        sort( a+1 , a+n+1 );
        for ( int i=1,p=0 ; i<=n ; i++ )
        {
            while ( p<a[i].val ) add( cal(p++)+1 , 1 );
            ans[a[i].idx] = sum( cal(p) );
        }
        for ( int i=1 ; i<=n ; i++ ) printf( "%d\n" , ans[i] );
    }
    return 0;
}

G题

题意:要求构造一颗树,这棵树n-1条要求,每个要求需要使得断开树中的一条边后分成的两个连通块中的最大下标值分别为a和b

思路:首先a,b之中必须要有一个值为n,因为两个连通块中至少有一个连通块中有n,然后将所有a取出来排序,用set维护未被使用掉的节点值,贪心构造这颗树就可以了

代码:

#include<bits/stdc++.h>
using  namespace  std;
const int maxn = 1010;
int a[maxn],ans[maxn]; set<int>s;
int main()
{
    for ( int n ; scanf( "%d" , &n )==1 ; )
    {
        bool ok = true;
        for ( int i=2 ; i<=n ; i++ )
        {
            scanf ( "%d" , &a[i] );
            int x; scanf( "%d" , &x );
            if ( x!=n ) ok = false;
        }
        if ( !ok )
        {
            printf( "NO\n" );
            continue;
        }
        for ( int i=1 ; i<=n ; i++ ) s.insert(i);
        sort( a+2 , a+n+1 ); ans[1] = a[2]; s.erase(a[2]);
        for ( int i=3 ; i<=n ; i++ )
        {
            if ( a[i]!=a[i-1] )
            {
                ans[i-1] = a[i]; s.erase(a[i]);
            }
            else
            {
                if ( *(s.begin())<a[i] )
                {
                    ans[i-1] = *(s.begin());
                    s.erase(s.begin());
                }
                else
                {
                    ok = false;
                    break;
                }
            }
        }
        ans[n] = n;
        if ( ok )
        {
            printf( "YES\n" );
            for ( int i=1 ; i< n ; i++ )
                printf( "%d %d\n" , ans[i] , ans[i+1] );
        }
        else
            printf( "NO\n" );
    }
    return 0;
}

H题

题意:有一个矩形,挖掉一个小矩形之后用1*2的方块填充这个矩形,填充不完整的地方(会留有一些1*1)需要将1*2的方块打碎成两块用来填充,问打碎多少块1*2的方块

思路:直接计算

代码:

#include<bits/stdc++.h>
using  namespace std;
int Max( int a , int b ){ return a>b?a:b; }
int Min( int a , int b ){ return a<b?a:b; }
int cal( int n ){ return n%2; }
int main()
{
    for ( int n,m ; scanf( "%d%d" , &n , &m )==2 ; )
    {
        int x1,y1,x2,y2; scanf( "%d%d%d%d" , &x1 , &y1 , &x2 , &y2 );
        int ans = (x2-x1+1)*(cal(y1-1)+cal(m-y2))+(n-x2+x1-1)*cal(m);
        printf( "%d\n" , (ans+1)/2 );
    }
    return 0;
}

I题

题意:计算最大值-最小值+1-n

思路:直接计算

代码:

#include<bits/stdc++.h>
using  namespace std;
int Max( int a , int b ){ return a>b?a:b; }
int Min( int a , int b ){ return a<b?a:b; }

int main()
{
    for ( int n ; scanf( "%d" , &n )==1 ; )
    {
        int minx = 1000000000,maxx = 1;
        for ( int i=1 ; i<=n ; i++ )
        {
            int x; scanf( "%d" , &x );
            minx = Min( minx , x );
            maxx = Max( maxx , x );
        }
        printf( "%d\n" , maxx-minx+1-n );
    }
    return 0;
}

J题

题意:求W<=A且H<=B使得W/H==X/Y的组数

思路:将X和Y约分后取Min(A/X,B/Y)

代码:

#include<bits/stdc++.h>
using  namespace std;
typedef long long LL;
int Max( int a , int b ){ return a>b?a:b; }
LL Min( LL a , LL b ){ return a<b?a:b; }
LL Gcd( LL a , LL b ){ return b==0?a:Gcd( b , a%b ); }
int main()
{
    for( LL a,b,x,y ; scanf( "%I64d%I64d%I64d%I64d" , &a , &b , &x , &y )==4 ; )
    {
        LL t = Gcd( x , y ); x /= t; y /= t;
        printf ( "%I64d\n" , Min( a/x ,  b/y ) );
    }
    return 0;
}

K题

题意:可以将n个数进行区间分割要求每个区间的中位数>=m问最多可以分成多少个区间

思路:定义dp[i]表示区间[1,i]最大可以分割的区间数,每次加入新节点时,向前枚举区间左界进行dp

代码:

#include<bits/stdc++.h>
using  namespace  std;
const int maxn = 5010;
int Max( int a , int b ){ return a>b?a:b; }
int n,m,dp[maxn],sum[maxn];
int main()
{
    for ( int n,m ; scanf( "%d%d" , &n , &m )==2 ; )
    {
        dp[0] = 0,sum[0] = 0;
        for ( int i=1 ; i<=n ; i++ )
        {
            int x; scanf( "%d" , &x );
            if ( x>=m ) sum[i] = sum[i-1]+1;
            else sum[i] = sum[i-1];
        }
        for ( int i=1 ; i<=n ; i++ )
        {
            dp[i] = 0;
            for ( int j=0 ; j<i ; j++ )
                if ( sum[i]-sum[j]>=(i-j)/2+1&&(j==0||dp[j]!=0) )
                    dp[i] = Max( dp[i] , dp[j]+1 );
        }
        printf( "%d\n" , dp[n] );
    }
    return 0;
}

L题

题意:有两个反射镜面,下镜面有n个点,上镜面有m个,选任意点和任意角度射入一条光线,求经过的最多点数

思路:因为在枚举每个步长之后其奇数倍的步长就不需要再枚举了,所以枚举从下镜面当上镜面的变化步长只需枚举logn个,维护相应的数据答案,注意光线是可以垂直的

代码:

#include<bits/stdc++.h>
using  namespace  std;
const int maxn = 100010;
int Max( int a , int b ){ return a>b?a:b; }
int n,a[maxn],aa[maxn];
int m,b[maxn],bb[maxn];
int main()
{
    for ( int t ; scanf( "%d%d" , &n , &t )==2 ; )
    {
        int ans = 0;
        for  ( int i=1 ; i<=n ; i++ ) scanf( "%d" , &a[i] );
        scanf( "%d%d" , &m , &t );
        for  ( int i=1 ; i<=m ; i++ ) scanf( "%d" , &b[i] );
        for  ( int S=1,B=2 ; S<=1000000000 ; S<<=1,B<<=1 )
        {
            for ( int i=1 ; i<=n ; i++ ) aa[i] = a[i]%B;
            for ( int i=1 ; i<=m ; i++ ) bb[i] = b[i]%B;
            sort( aa+1 , aa+n+1 );
            sort( bb+1 , bb+m+1 );
            int bl = 1;
            for ( int i=1 ; i<=n ; i++ )
            {
                int l=i,r=i;
                while ( r+1<=n&&aa[r+1]==aa[l] )  r++;
                while ( bl <=m&&bb[bl]<aa[l]+S ) bl++;
                if ( bl<=m&&bb[bl]==aa[l]+S )
                {
                    int br = bl;
                    while ( br+1<=m&&bb[br+1]==bb[bl] ) br++;
                    ans = Max( ans , r-l+1+br-bl+1 );
                    bl = br+1;
                }
                else
                    ans = Max( ans , r-l+1 );
                i = r;
            }
            int al = 1;
            for ( int i=1 ; i<=m ; i++ )
            {
                int l=i,r=i;
                while ( r+1<=m&&bb[r+1]==bb[l] )  r++;
                while ( al <=n&&aa[al]<bb[l]+S ) al++;
                if ( al<=n&&aa[al]==bb[l]+S )
                {
                    int ar = al;
                    while ( ar+1<=n&&aa[ar+1]==aa[al] ) ar++;
                    ans = Max( ans , r-l+1+ar-al+1 );
                    al = ar+1;
                }
                else
                    ans = Max( ans , r-l+1 );
                i = r;
            }
        }
        for ( int i=1,j=1 ; i<=n ; i++ )
        {
            while ( j<=m&&b[j]<a[i] ) j++;
            if ( j<=m&&b[j]==a[i] ) ans = Max( ans , 2 );
        }
        printf ( "%d\n" , ans );
    }
    return 0;
}

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值