题目大意:
城中的市民已经无法忍受市长候选人到处乱贴竞选海报了,因此地方议会决定建立一座竞选墙以供竞选者贴海报,并制定以下规则:
每位竞选人只能贴一张海报;
海报的高度和墙的高度一致,宽度是一个正整数;
墙的总长为10,000,000,以1为单位;
海报必须占据连续的一段,不能分为多张;
海报将按顺序贴,可以覆盖别人已经占据的位置,大多数海报的宽度都是不一样的,因此大家都很期待在竞选前一天有哪些海报是可见的(完整或者部分被覆盖(没有完全被覆盖));
现有多个测例(测例数为c,题中给出),每个测例中都会告诉总共有多少个(n个,n ≤ 10,000)候选人(即会贴出多少张海报),接着按顺序给出每张海报的左边和右边界限在墙上的坐标,试求出最后总共有多少张海报可见。
注释代码:
/*
* Problem ID : POJ 2528 Mayor's posters
* Author : Lirx.t.Una
* Language : G++
* Run Time : 63 ms
* Run Memory : 33864 KB
*/
#pragma G++ optimize("O2")
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define TRUE 1
#define FALSE 0
//可看成是颜色覆盖问题,NONE表示无色
#define NONE 0
/*
*==由于题目要求空间不得大于65536KB,
*==但是竞选墙的宽度为10,000,000,过大,
*==而实际海报数有很少,只要10,000张,因此
*==最多只会得到200,000中不同的坐标,也就是说
*==10,000,000个坐标中最多只会用到200,000个,
*==因此可以将得到的这200,000个坐标(去掉其中
*==重复的),映射到1-200,000这个新的坐标系中
*==然后对该新的坐标系构建线段树,这样就不会
*==超出空间要求了
*/
//maximum number of posters,海报的最大数量,10,000 + 1
#define MAXPOSTERN 10001
//maxumum transformed coordinate,最大的变换坐标
#define MAXXCORD 20001
//线段树的最大范围
//65536 = 1 << ( 1 + ceil( log2(20000) ) )
#define MAXSEGSIZE 65536
//最大原坐标
//用于将原坐标映射到压缩坐标
#define MAXCORD 10000001
typedef char BOOL;
typedef struct {
int lft;
int rht;
int col;//color,段的颜色
} Seg;
int lft[MAXPOSTERN];//lft[i]表示第i张海报的左界坐标
int rht[MAXPOSTERN];//rht[i]表示第i张海报的右界坐标
BOOL bcl[MAXPOSTERN];//be calculate,bcl[i]表示
//第i张海报所代表的颜色是否被计算过
int cord[MAXXCORD];//coordinate,记录200,000个坐标值
int xcod[MAXCORD];//transformed coordinate,变换压缩坐标
//xcod[ 老坐标 ] = 变换后的新坐标
Seg s[MAXSEGSIZE];
int ans;//最后又多少张海报可见
void
build( int r, int lft, int rht ) {
s[r].lft = lft;
s[r].rht = rht;
s[r].col = NONE;
if ( lft == rht )
return ;
int lc;
int mid;
lc = r + r;
mid = ( lft + rht ) >> 1;
build( lc, lft, mid );
build( lc + 1, mid + 1, rht );
}
void
paint( int r, int lft, int rht, int col ) {//给线段树上色,就是update
//给以r为根的线段树的lft-rht区间内上col这种颜色
if ( lft == s[r].lft && rht == s[r].rht ) {
//若上色区间刚好等于当前结点所表示的区间,则直接对该区间上色
s[r].col = col;
return ;
}
int lc, rc;//左右子树
int mid;//当前结点所表示的段的中间位置
lc = r + r;
rc = lc + 1;
mid = s[lc].rht;
if ( s[r].col ) {//若当前结点已经上过色了
//但是目前又不能完全覆盖该段,只能部分覆盖该段
//因此只能先将当前结点的颜色传递给下面的段
//然后再在下面的段继续上色
//同时也要将当前段重新刷新为无色
s[lc].col = s[r].col;
s[rc].col = s[r].col;
s[r].col = NONE;
}
if ( rht <= mid ) {//若上色区间在左半段
paint( lc, lft, rht, col );
return ;
}
if ( lft > mid ) {//上色区间在右半段
paint( rc, lft, rht, col );
return ;
}
//上色区间跨越中点
paint( lc, lft, mid, col );
paint( rc, mid + 1, rht, col );
}
void
cal(int r) {//calculate,计算有多少海报可见
int col;
col = s[r].col;//记录了根结点的颜色
if (col) {//若根结点有色,那就代表根结点
//所代表的段都被覆盖了这种颜色,那么
//下面的段就不用再检查了
if ( !bcl[col] ) {//若该颜色没被计算过
//那么就记录一下
ans++;
bcl[col] = TRUE;
}
return ;
}
int lc;
//若当前结点无色,说明有可能是它的子
//段被上色了,然后继续检查其子段的颜色即可
lc = r + r;
cal(lc);
cal( lc + 1 );
}
int
fcmp(const void *a, const void *b) {
return *(int *)a - *(int *)b;
}
int
main() {
int c;
int n;
int i, j;
scanf("%d", &c);
while ( c-- ) {
scanf("%d", &n);
memset(bcl + 1, FALSE, n * sizeof(BOOL));
for ( i = 1, j = 1; i <= n; i++, j++ ) {
scanf("%d%d", lft + i, rht + i);
//将所有出现的坐标都记录在一个数组中
cord[j++] = lft[i];
cord[j] = rht[i];
}
//对该数组排序后挑出互不相同的坐标
qsort(cord + 1, n + n, sizeof(int), &fcmp);
for ( j = 1, i = 1; j <= n + n; j++, i++ ) {
cord[i] = cord[j];
xcod[ cord[i] ] = i;//将这些互不相同的坐标映射到
//压缩后的坐标系中
while ( j < n + n && cord[j] == cord[j + 1] )//排除相同的坐标
j++;
}
build( 1, 1, i - 1 );
for ( i = 1; i <= n; i++ )
paint( 1, xcod[ lft[i] ], xcod[ rht[i] ], i );
ans = 0;
cal(1);
printf("%d\n", ans);
}
return 0;
}
无注释代码:
#pragma G++ optimize("O2")
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define NONE 0
#define MAXPOSTERN 10001
#define MAXXCORD 20001
#define MAXSEGSIZE 65536
#define MAXCORD 10000001
typedef char BOOL;
typedef struct {
int lft;
int rht;
int col;
} Seg;
int lft[MAXPOSTERN];
int rht[MAXPOSTERN];
BOOL bcl[MAXPOSTERN];
int cord[MAXXCORD];
int xcod[MAXCORD];
Seg s[MAXSEGSIZE];
int ans;
void
build( int r, int lft, int rht ) {
s[r].lft = lft;
s[r].rht = rht;
s[r].col = NONE;
if ( lft == rht )
return ;
int lc;
int mid;
lc = r + r;
mid = ( lft + rht ) >> 1;
build( lc, lft, mid );
build( lc + 1, mid + 1, rht );
}
void
paint( int r, int lft, int rht, int col ) {
if ( lft == s[r].lft && rht == s[r].rht ) {
s[r].col = col;
return ;
}
int lc, rc;
int mid;
lc = r + r;
rc = lc + 1;
mid = s[lc].rht;
if ( s[r].col ) {
s[lc].col = s[r].col;
s[rc].col = s[r].col;
s[r].col = NONE;
}
if ( rht <= mid ) {
paint( lc, lft, rht, col );
return ;
}
if ( lft > mid ) {
paint( rc, lft, rht, col );
return ;
}
paint( lc, lft, mid, col );
paint( rc, mid + 1, rht, col );
}
void
cal(int r) {
int col;
col = s[r].col;
if (col) {
if ( !bcl[col] ) {
ans++;
bcl[col] = TRUE;
}
return ;
}
int lc;
lc = r + r;
cal(lc);
cal( lc + 1 );
}
int
fcmp(const void *a, const void *b) {
return *(int *)a - *(int *)b;
}
int
main() {
int c;
int n;
int i, j;
scanf("%d", &c);
while ( c-- ) {
scanf("%d", &n);
memset(bcl + 1, FALSE, n * sizeof(BOOL));
for ( i = 1, j = 1; i <= n; i++, j++ ) {
scanf("%d%d", lft + i, rht + i);
cord[j++] = lft[i];
cord[j] = rht[i];
}
qsort(cord + 1, n + n, sizeof(int), &fcmp);
for ( j = 1, i = 1; j <= n + n; j++, i++ ) {
cord[i] = cord[j];
xcod[ cord[i] ] = i;
while ( j < n + n && cord[j] == cord[j + 1] )
j++;
}
build( 1, 1, i - 1 );
for ( i = 1; i <= n; i++ )
paint( 1, xcod[ lft[i] ], xcod[ rht[i] ], i );
ans = 0;
cal(1);
printf("%d\n", ans);
}
return 0;
}
STL优化:
注释代码:
/*
* Problem ID : POJ 2528 Mayor's posters
* Author : Lirx.t.Una
* Language : G++
* Run Time : 47 ms
* Run Memory : 33508 KB
*/
#pragma G++ optimize("O2")
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define MAXPOSTN 10000
#define MAXSEGSIZE 65536
#define MAXCORD 10000000
#define LFT(T) ( (T) << 1 )
#define RHT(T) ( LFT(T) | 1 )
using namespace std;
//lft and rht point of posters
//每张海报的左右端点坐标
int pl[MAXPOSTN + 1];
int pr[MAXPOSTN + 1];
//将所有端点坐标都保存在x中
int x[1 + ( MAXPOSTN << 1 ) ];
//将海报中出现的坐标都映射到一个小范围中以减小
//树的大小
int hash[MAXCORD + 1];
//cover,即线段树
//true表示该结点表示的区间被海报完全覆盖了,false表示没有被覆盖过
bool cov[MAXSEGSIZE];
bool
post( int tree, int al, int ar, int lft, int rht ) {
//adding lft and rht,表示将海报[lft, rht]贴到
//墙[lft, rht]中
//!!注意,这里的算法是按照相反的顺序进行检查
//即在算法中先将后面贴的海报反过来先贴到墙上
//如果当前海报所占区域之前已经被其它海报覆盖过了则
//当前海报必然无法露在外面
//已经被覆盖,所以[al, ar]无法露在外面
if ( cov[tree] ) return false;
//表示该区域没有被完全覆盖,因此当前海报必然可以露在外面一部分
if ( lft == al && ar == rht ) return cov[tree] = true;
int mid;
bool res;
mid = ( lft + rht ) >> 1;
if ( ar <= mid )
res = post( LFT(tree), al, ar, lft, mid );
else if ( al > mid )
res = post( RHT(tree), al, ar, mid + 1, rht );
else {
bool bl, br;
//!!!注意,不能res = bl_post() || br_post()
//因为||具有短路特性,br_post不一定执行,这会导致右边可能没有被正常覆盖,从而影响后面的结果
bl = post( LFT(tree), al, mid, lft, mid );
br = post( RHT(tree), mid + 1, ar, mid + 1, rht );
res = bl || br;//两边中只要有一边没被完全覆盖则表示该海报有一部分可以露在外面
}
cov[tree] = cov[ LFT(tree) ] && cov[ RHT(tree) ];//注意!!还需更新当前结点覆盖情况,update的传统操作!!
//如果两边在之前的操作中都被覆盖了,则代表当前结点区间肯定也被完全覆盖了
return res;
}
int
main() {
int t;//测例数
int n;//海报数
int nn;//最终海报端点坐标不重复的个数
int i;//计数变量
int ans;
scanf("%d", &t);
while ( t-- ) {
memset(cov, false, sizeof(cov));//相当于build,初始化为全都没被覆盖
scanf("%d", &n);
nn = 0;//进行离散化
for ( i = 1; i <= n; i++ ) {
scanf("%d%d", pl + i, pr + i);
x[nn++] = pl[i];
x[nn++] = pr[i];
}
sort(x, x + nn);//排序
nn = unique(x, x + nn) - x;//单一化
for ( i = 0; i < nn; i++ ) hash[ x[i] ] = i + 1;//映射成更小的范围
ans = 0;
for ( i = n; i > 0; i-- )//!!!注意,从后往前贴!!!
if ( post( 1, hash[ pl[i] ], hash[ pr[i] ], 1, nn ) )
ans++;
printf("%d\n", ans);
}
return 0;
}
无注释代码:
#pragma G++ optimize("O2")
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define MAXPOSTN 10000
#define MAXSEGSIZE 65536
#define MAXCORD 10000000
#define LFT(T) ( (T) << 1 )
#define RHT(T) ( LFT(T) | 1 )
using namespace std;
int pl[MAXPOSTN + 1];
int pr[MAXPOSTN + 1];
int x[1 + ( MAXPOSTN << 1 ) ];
int hash[MAXCORD + 1];
bool cov[MAXSEGSIZE];
bool
post( int tree, int al, int ar, int lft, int rht ) {
if ( cov[tree] ) return false;
if ( lft == al && ar == rht ) return cov[tree] = true;
int mid;
bool res;
mid = ( lft + rht ) >> 1;
if ( ar <= mid )
res = post( LFT(tree), al, ar, lft, mid );
else if ( al > mid )
res = post( RHT(tree), al, ar, mid + 1, rht );
else {
bool bl, br;
bl = post( LFT(tree), al, mid, lft, mid );
br = post( RHT(tree), mid + 1, ar, mid + 1, rht );
res = bl || br;
}
cov[tree] = cov[ LFT(tree) ] && cov[ RHT(tree) ];
return res;
}
int
main() {
int t;
int n;
int nn;
int i;
int ans;
scanf("%d", &t);
while ( t-- ) {
memset(cov, false, sizeof(cov));
scanf("%d", &n);
nn = 0;
for ( i = 1; i <= n; i++ ) {
scanf("%d%d", pl + i, pr + i);
x[nn++] = pl[i];
x[nn++] = pr[i];
}
sort(x, x + nn);
nn = unique(x, x + nn) - x;
for ( i = 0; i < nn; i++ ) hash[ x[i] ] = i + 1;
ans = 0;
for ( i = n; i > 0; i-- )
if ( post( 1, hash[ pl[i] ], hash[ pr[i] ], 1, nn ) )
ans++;
printf("%d\n", ans);
}
return 0;
}
单词解释:
poster:n, 海报,广告
mayor:n, 市长
citizen:n, 市民,公民
byte:n, 字节
can not stand that:不能忍受....
candidate:n, 候选人
mayoral:adj, 市长的
election:n, 选举
mayoral election:n, 市长选举
campaign:n, 竞选运动
electoral:adj, 选举的
electoral poster:n, 选举海报
whim:n, 一时兴起
at one;s whim:在某人一时兴起时
council:n, 委员会,地方议会
exactly:adv, 恰好的,精确的
segment:n, 段,部分
contiguous:adj, 连续的,接触的
widely:adv, 广泛的
differ in..:...在...方面不同
moreover:adv, 而且,此外
occupy:vt, 占领,占据
visible:adj, 可见的
subsequent:adj, 后来的,随后的
respectively:adv, 分别的,各自的
collegiate:adj, 大学的
Alberta:n, 地名,亚伯达,加拿大西部的一个省