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(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或树链剖分,还没有了解,先去预习