NOIP模拟赛0816

T1 [USACO08FEB]酒店Hotel

线段树模板题,处理从左端点开始连续的空位,右端点开始连续的空位,整个区间最长连续空位.

然后其它的差不多

有两个点写错了:

1.lazy的标记,要有3种情况,全空,全满,有空有满,因为有两种操作

2.细节

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
const int MAXN = 50003;
struct node{
    int l , r;
    int lazy;
    int zre , lre , rre;
    int zl;
}tre[MAXN*4];
int ml , mr;
int n , m , o = 1;
void bu( int i , int ld , int r ){
    tre[i].l = ld , tre[i].r = r;
    tre[i].lazy = 0;
    tre[i].zre = tre[i].lre = tre[i].rre = r - ld + 1;
    tre[i].zl = ld;
    if( ld == r )
        return ;
    int mid = ( ld + r ) / 2 ;
    bu( i * 2 , ld , mid );
    bu( i * 2 + 1 , mid + 1 , r );
    return ;
}
void ask( int i , int x ){
    if( tre[i].lazy == 1 ){
        tre[i*2].lazy = tre[i].lazy , tre[i*2+1].lazy = tre[i].lazy;
        tre[i*2].zre = tre[i*2].lre = tre[i*2].rre = 0;
        tre[i*2+1].zre = tre[i*2+1].lre = tre[i*2+1].rre = 0;
        tre[i*2].zl = tre[i*2+1].zl = 0;
        tre[i].lazy = 0;
    }
    else if( tre[i].lazy == 2 ){
        tre[i*2].zre = tre[i*2].rre = tre[i*2].lre = tre[i*2].r - tre[i*2].l + 1 ;
        tre[i*2].lazy = 2 , tre[i*2].zl = tre[i*2].l;
        tre[i*2+1].zre = tre[i*2+1].rre = tre[i*2+1].lre = tre[i*2+1].r - tre[i*2+1].l + 1 ;
        tre[i*2+1].lazy = 2 , tre[i*2+1].zl = tre[i*2+1].l;
        tre[i].lazy = 0;
    }
    if( tre[i].lre >= x ){
        ml = tre[i].l , mr = tre[i].l + x - 1;
        return ;
    }
    else if( tre[i*2].zre >= x )
        ask( i * 2 , x );
    else if( tre[i*2].rre + tre[i*2+1].lre >= x ){
        ml = tre[i*2].r - tre[i*2].rre + 1 , mr = ml + x - 1;
        return ;
    }
    else
        ask( i * 2 + 1 , x ) ;
}
void query( int i , int l , int r ){
    if( tre[i].r < l || tre[i].l > r )
        return ;
    if( l <= tre[i].l && r >= tre[i].r ){
        tre[i].lazy = 1;
        tre[i].zre = tre[i].rre = tre[i].lre = 0;
        return ;
    }
    if( tre[i].lazy == 1 ){
        tre[i*2].lazy = tre[i].lazy , tre[i*2+1].lazy = tre[i].lazy;
        tre[i*2].zre = tre[i*2].lre = tre[i*2].rre = 0;
        tre[i*2+1].zre = tre[i*2+1].lre = tre[i*2+1].rre = 0;
        tre[i*2].zl = tre[i*2+1].zl = 0;
        tre[i].lazy = 0;
    }
    else if( tre[i].lazy == 2 ){
        tre[i*2].zre = tre[i*2].rre = tre[i*2].lre = tre[i*2].r - tre[i*2].l + 1 ;
        tre[i*2].lazy = 2 , tre[i*2].zl = tre[i*2].l;
        tre[i*2+1].zre = tre[i*2+1].rre = tre[i*2+1].lre = tre[i*2+1].r - tre[i*2+1].l + 1 ;
        tre[i*2+1].lazy = 2 , tre[i*2+1].zl = tre[i*2+1].l;
        tre[i].lazy = 0;
    }
    query( i * 2 , l ,  r );
    query( i * 2 + 1 , l , r );
    if( tre[i*2].zre >= tre[i*2+1].zre )
        tre[i].zre = tre[i*2].zre , tre[i].zl = tre[i*2].zl;
    else
        tre[i].zre = tre[i*2+1].zre , tre[i].zl = tre[i*2+1].zl;
    if( tre[i].zre < tre[i*2].rre + tre[i*2+1].lre || ( tre[i].zre == tre[i*2].rre + tre[i*2+1].lre && tre[i].zl > tre[i*2].r - tre[i*2].rre + 1 ) )
        tre[i].zre = tre[i*2].rre + tre[i*2+1].lre , tre[i].zl = tre[i*2].r - tre[i*2].rre + 1;
    tre[i].lre = tre[i*2].lre;
    if( tre[i].zl == tre[i].l )
        tre[i].lre = max( tre[i].lre , tre[i].zre );
    tre[i].rre = tre[i*2+1].rre;
    if( tre[i].zl + tre[i].zre - 1 == tre[i].r && tre[i].zre > tre[i*2+1].rre )
        tre[i].rre = tre[i].zre;
}
void insert_( int i , int l , int r ){
    if( tre[i].r < l || tre[i].l > r )
        return ;
    if( l <= tre[i].l && r >= tre[i].r ){
        tre[i].lazy = 2;
        tre[i].zre = tre[i].lre = tre[i].rre = tre[i].r - tre[i].l + 1;
        tre[i].zl = tre[i].l;
        return ;
    }
    if( tre[i].lazy == 1 ){
        tre[i*2].lazy = tre[i].lazy , tre[i*2+1].lazy = tre[i].lazy;
        tre[i*2].zre = tre[i*2].lre = tre[i*2].rre = 0;
        tre[i*2+1].zre = tre[i*2+1].lre = tre[i*2+1].rre = 0;
        tre[i*2].zl = tre[i*2+1].zl = 0;
        tre[i].lazy = 0;
    }
    else if( tre[i].lazy == 2 ){
        tre[i*2].zre = tre[i*2].rre = tre[i*2].lre = tre[i*2].r - tre[i*2].l + 1 ;
        tre[i*2].lazy = 2 , tre[i*2].zl = tre[i*2].l;
        tre[i*2+1].zre = tre[i*2+1].rre = tre[i*2+1].lre = tre[i*2+1].r - tre[i*2+1].l + 1 ;
        tre[i*2+1].lazy = 2 , tre[i*2+1].zl = tre[i*2+1].l;
        tre[i].lazy = 0;
    }
    insert_( i * 2 , l , r );
    insert_( i * 2 + 1 , l , r );
    if( tre[i*2].zre >= tre[i*2+1].zre )
        tre[i].zre = tre[i*2].zre , tre[i].zl = tre[i*2].zl;
    else
        tre[i].zre = tre[i*2+1].zre , tre[i].zl = tre[i*2+1].zl;
    if( tre[i].zre < tre[i*2].rre + tre[i*2+1].lre || ( tre[i].zre == tre[i*2].rre + tre[i*2+1].lre && tre[i].zl > tre[i*2].r - tre[i*2].rre + 1 ) )
        tre[i].zre = tre[i*2].rre + tre[i*2+1].lre , tre[i].zl = tre[i*2].r - tre[i*2].rre + 1;
    tre[i].lre = tre[i*2].lre;
    if( tre[i].zl == tre[i].l )
        tre[i].lre = max( tre[i].lre , tre[i].zre );
    tre[i].rre = tre[i*2+1].rre;
    if( tre[i].zl + tre[i].zre - 1 == tre[i].r && tre[i].zre > tre[i*2+1].rre )
        tre[i].rre = tre[i].zre;
}
int main(){
    //freopen( "hotel.in" , "r" , stdin );
    //freopen( "hotel.out" , "w" , stdout );
    scanf( "%d%d" , &n , &m );
    bu( 1 , 1 , n );
    for( int i = 1 ; i <= m ; i ++ ){
        int ques , x;
        scanf( "%d%d" , &ques , &x );
        if( ques == 1 ){
            ml = 0;
            if( tre[1].zre < x ){
                printf( "0\n" );
                continue;
            }
            ask( 1 , x );
            printf( "%d\n" , ml );
            query( 1 , ml , mr );
        }
        else{
            int y;
            scanf( "%d" , &y );
            insert_( 1 , x , x + y - 1 );
        }
    }
    return 0;
}

总结:

自己的基础知识点还弄懂得不是很完整。

同时,自己的经验还不足够,这样一道水题竟然自己卡了一半的时间

T2:序列

题目描述

给定n个正整数的序列a1,a2,a3…an,对该序列可执行下面的操作: 选择一个大于k的正整数ai,将ai的值减去1;选择ai-1或ai+1中的一个值加上1。 共给出m个正整数k,对于每次给定的正整数k,经过以上操作一定次数后,求出最长的一个连续子序列,使得这个子序列的每个数都不小于给定的k

输入格式

第一行两个正整数n和m。 第二行n个正整数,第i个正整数表示ai ; 第三行m个正整数,第i个正整数表示第i次给定的k。

输出格式

共一行,输出m个正整数,第i个数表示对于第i次给定的k,经过一定次数操作后,最长连续子序列的长度。

样例

样例输入1

5 6
1 2 1 1 5
1 2 3 4 5 6

输出样例1

5 5 2 1 1 0

数据范围与提示

对于 30%的数据,n<30 对于 50%的数据,n<=2000 对于 100%的数据,n<=1,000,000 ,m<= 50, k <= 10^9, ai <= 10^9

首先把前缀和算出来,然后再定义b[i] = sum[i] - i * k(k是当前k,在线处理)

如果有b[i]-b[j]>=0 那么就说明再i到j+1之间的点都可以满足条件

就是sum[i] - sum[j] - k * ( i - j )

因为这段区间内是一定可以互相转换的

所以就有答案了

可是这样明显是O(n^{2}m)的,超时

我们把上面这样的j称为左端点

那么可以维护一个只能做左端点的点集,且从小到大

我们想怎样一个点只能做左端点,首先,它前面的b值都会大于它的b值

其次,为什么维护的是只能做左端点点集呢?如果一个点i可当左也可当右,那么当它做左端点与一个点j围成一个区间时,是不优的,因为当它做右端点的时候与之匹配的左端点k,明显b[k]<b[i]<b[j],那么明显可以直接用j与k围成区间

如果只有左端点,则我们找不到这样一个k,那么i就是最优的了

同时,由于数据很卡,我们还要有一个优化

就是如果i的左端点是k,i-1的左端点不能是比k大的数,显然不优

所以可以从后往前找,如果这个找到一个左端点(在点集中),那么比它下标大的点都可以删掉了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define ll long long
const int MAXN = 1000003;
ll n , m , k;
ll a[MAXN] , sum[MAXN];
ll b;
void read( ll &x ){
    x = 0;char s = getchar();
    while( s < '0' || s >'9' ){
        s = getchar();
    }
    while( s >= '0' && s <= '9' ){
        x= x * 10 + s - '0';
        s = getchar();
    }
}
struct node{
    ll x;
    int id;
}st[MAXN];
int cnt;
ll max_( ll x , ll y ){
    if( x > y )
        return x;
    return y;
}
int main(){
    //freopen("sequence.in","r",stdin);
    //freopen("sequence.out","w",stdout);
    read( n );read( m );
    for( int i = 1 ; i <= n ; i ++ ){
        read( a[i] );
        sum[i] = sum[i-1] + a[i];
    }
    for( int i = m ; i >= 1 ; i -- ){
        read( k );
        int ans = 0;
        cnt = 0;
        st[cnt].x = 0 , st[cnt].id = 0;
        ll tmp = 0 ;
        for( int j = 1 ; j <= n ; j ++ ){
            tmp += k;
            b = sum[j] - tmp;
            if( b < st[cnt].x ){
                st[++cnt].x = b , st[cnt].id = j;
            }
        }
        for( int j = n ; j >= 1 ; j -- ){
            b = sum[j] - tmp;
            tmp -= k;
            if( b > st[0].x ){
                ans = max_( ans , j );
            }
            else{
                while( cnt >= 0 && st[cnt].x <= b )
                    cnt --;
                cnt ++;
                ans = max_( ans , j - st[cnt].id );
            }
        }
        printf( "%d " , ans );
    }

    return 0;
}

T3:长跑

题目描述

某校开展了同学们喜闻乐见的阳光长跑活动。为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动。一时间操场上熙熙攘攘,摩肩接踵,盛况空前。   为了让同学们更好地监督自己,学校推行了刷卡机制。   学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。   有以下三类事件:   1、修建了一条连接A地点和B地点的跑道。   2、A点的刷卡机台数变为了B。   3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:   当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。   为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。

输入格式

输入的第一行包含两个正整数n,m(n<=100000,m<=300000),表示地点的个数和操作的个数。   第二行包含n个非负整数,其中第i个数为第i个地点最开始刷卡机的台数。   接下来有m行,每行包含三个非负整数P,A,B,P为事件类型,A,B为事件的两个参数。   最初所有地点之间都没有跑道。   每行相邻的两个数之间均用一个空格隔开。表示地点编号的数均在1到n之间,每个地点的刷卡机台数始终不超过10000,P=1,2,3。

输出格式

输出的行数等于第3类事件的个数,每行表示一个第3类事件。如果该情况下存在一种设定跑道方向的方案和路径的方案,可以到达,则输出最多可以刷卡的次数。如果A不能到达B,则输出-1。

样例

输入样例1

9 31
	10 20 30 40 50 60 70 80 90
	3 1 2
	1 1 3
	1 1 2
	1 8 9
	1 2 4
	1 2 5
	1 4 6
	1 4 7
	3 1 8
	3 8 8
	1 8 9
	3 8 8
	3 7 5
	3 7 3
	1 4 1
	3 7 5
	3 7 3
	1 5 7
	3 6 5
	3 3 6
	1 2 4
	1 5 5
	3 3 6
	2 8 180
	3 8 8
	2 9 190
	3 9 9
	2 5 150
	3 3 6
	2 1 210
	3 3 6

输出样例1:

        -1
        -1
        80
	170
	180
	170
	190
	170
	250
	280
	280
	270
	370
	380
	580

要用LCT或树链剖分,还没有了解,先去预习

发布了68 篇原创文章 · 获赞 7 · 访问量 3847

猜你喜欢

转载自blog.csdn.net/weixin_43823476/article/details/99683290