全球覆盖

题目

题目数据范围与提示
对于前 10 % 10\% 的数据, n 10 n ≤ 10
对于前 20 % 20\% 的数据, n 20 n ≤ 20
对于另 50 % 50\% 的数据, n 3000 n ≤ 3000
对于 100 % 100\% 的数据, n 500000 , 2 X , Y 1 0 9 , 0 x 0 < x 1 < X , 0 y 0 < y 1 < Y n ≤ 500000,2 ≤ X, Y ≤ 10^9, 0 ≤ x_0 < x_1 < X, 0 ≤ y_0 < y_1 < Y

思路壹

不难发现,“矩形”内的格子分成了四类, x 0 < x < x 1 x_0<x<x_1 是否成立、 y 0 < y < y 1 y_0<y<y_1 是否成立。

所以 x , y x,y 上的限制是独立的。问题转化为,对于 n n 个区间,将其中一些设置为绿色,另外的为红色,格子位于所有绿色区间内,且不位于红色区间内,则可产生贡献。最大化之。

我的想法是,枚举一个格子,这个格子是最终的可用格子中最靠右的一个。那么显然,区间有三类:

  • 在该格子右边的:涂成红色即可。无影响。
  • 在该格子左边的:涂成红色。要减去所有红色区间的并集。
  • 包含该格子的:涂成绿色。期望答案为这种区间的交集。

而这三种都可以动态维护。并集用线段树实现。该格子显然会是某个区间的右端点(考虑其为绿色)或左端点(考虑其为红色)。复杂度 O ( n log n ) \mathcal O(n\log n) 。但是过不了。常数大。

代码壹

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
const int cache_len = 5000000;
char buffer[cache_len], *S, *T; 
inline char getChar(){
    if (S == T){ // 缓存耗尽
		S = buffer; // 只不过是从头再来
        T = S+fread(buffer,1,cache_len,stdin);
        if (S == T) return EOF; // end of file
    }
    return *(S ++);
}
inline int readint(){
    int a = 0; char c = getChar(), f = 1;
	for(; c<'0'||c>'9'; c=getChar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getChar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

struct Range{
	int l, r;
	operator int()const{ return r; }
};
const int SqrtN = 100000;
vector< int > bucket[SqrtN];
void qkSort(int *l,int *r){
	for(int *p=l; p!=r; ++p)
		bucket[(*p)%SqrtN].push_back(*p);
	for(int *p=l,i=0; i<SqrtN; ++i){
		for(auto x : bucket[i])
			*(p ++) = x;
		bucket[i].clear();
	}

	for(int *p=l; p!=r; ++p)
		bucket[(*p)/SqrtN].push_back(*p);
	for(int *p=l,i=0; i<SqrtN; ++i){
		for(auto x : bucket[i])
			*(p ++) = x;
		bucket[i].clear();
	}
}
vector< Range > bukcet[SqrtN];
void qkSort(Range *l,Range *r){
	Range *p; // 一个指针一直用
	for(p=l; p!=r; ++p)
		bukcet[(*p)%SqrtN].push_back(*p);
	p = l; // 对后半部分进行排序
	for(int i=0; i<SqrtN; ++i){
		for(auto x : bukcet[i])
			*(p ++) = x;
		bukcet[i].clear();
	}

	for(p=l; p!=r; ++p)
		bukcet[(*p)/SqrtN].push_back(*p);
	p = l; // 对前半部分进行排序
	for(int i=0; i<SqrtN; ++i){
		for(auto x : bukcet[i])
			*(p ++) = x;
		bukcet[i].clear();
	}
}

const int MaxN = 500005;

int tmp[MaxN<<1];
namespace SgTree{
	int v[MaxN<<2];
	void clear(int l,int r){
		l <<= 1, r <<= 1;
		for(int i=l; i<=r; ++i)
			v[i] = 0; // 啥也没有
	}
	int modify(int ql,int qr,int l,int r){
		int t = (l+r)|(l!=r);
		if(qr < l || r < ql) return t;
		if(v[t] == tmp[r+1]-tmp[l])
			return t; // 已经满了
		if(ql <= l && r <= qr){
			v[t] = tmp[r+1]-tmp[l];
			return t;
		}
		int m = (l+r)>>1;
		int lson = modify(ql,qr,l,m);
		int rson = modify(ql,qr,m+1,r);
		v[t] = v[lson]+v[rson]; // pushUp
		return t; // 返回当前节点编号
	}
	int query(int ql,int qr,int l,int r){
		int t = (l+r)|(l!=r);
		if(ql <= l && r <= qr) return v[t];
		int m = (l+r)>>1;
		if(v[t] == tmp[r+1]-tmp[l])
			return tmp[qr+1]-tmp[ql];
		if(qr <= m) return query(ql,qr,l,m);
		if(m < ql) return query(ql,qr,m+1,r);
		return query(ql,m,l,m)+query(m+1,qr,m+1,r);
	}
}

multiset< int > ls; // 所有 r >= R 的 l
int solve(Range a[],int n){
	for(int i=0; i<n; ++i){
		tmp[i<<1] = a[i+1].l;
		tmp[i<<1|1] = a[i+1].r;
		swap(a[i+1].l,a[i+1].r);
	}
	qkSort(tmp,tmp+(n<<1));
	int m = unique(tmp,tmp+n*2)-tmp;
	qkSort(a+1,a+n+1); // 按照原来的 l 排序
	for(int i=1,x=0; i<=n; ++i){ // 离散化
		while(tmp[x] != a[i].r) ++ x;
		a[i].r = x; // 利用有序性直接求得哈希值
		swap(a[i].l,a[i].r); // 换回原序
	}
	qkSort(a+1,a+n+1); // 按照原来的 r 排序
	for(int i=1,x=0; i<=n; ++i){
		while(tmp[x] != a[i].r) ++ x;
		a[i].r = x; // 对右端点进行离散化
	}
	ls.clear(); // 所有左端点
	for(int i=1; i<=n; ++i)
		ls.insert(a[i].l);
	SgTree::clear(0,m-2);
	int ans = 0; // 返回值
	int pa = 1; // a 的指针
	for(int i=0; i<m; ++i){
		/* 右端点 R = i */ ;
		auto it = ls.lower_bound(i);
		if(it != ls.begin()){
			int L = *(-- it); // 目前的 l
// printf("try [%d, %d]\n",tmp[L],tmp[i]);
			ans = max(ans,tmp[i]-tmp[L]
				-SgTree::query(L,i-1,0,m-2));
		}
		while(pa <= n && a[pa].r == i){
			ls.erase(ls.find(a[pa].l));
			SgTree::modify(a[pa].l,i-1,0,m-2);
			++ pa;
		}
	}
// printf("ans = %d\n",ans);
	return ans;
}

Range x[MaxN], y[MaxN];
int main(){
	freopen("globe.in","r",stdin);
	freopen("globe.out","w",stdout);
	int n = readint(), X = readint(), Y = readint();
	for(int i=1; i<=n; ++i){
		x[i].l = readint(), y[i].l = readint();
		x[i].r = readint(), y[i].r = readint();
	}
	++ n; // 加入地图边缘
	x[n].l = 0, x[n].r = X;
	y[n].l = 0, y[n].r = Y;
	printf("%lld\n",1ll*solve(x,n)*solve(y,n));
	return 0;
}

思路贰

枚举格子之后可以不用直接统计。而是转为求出区间的染色方案。可以哈希。

对于每一种染色方案取最大值即可。复杂度仍然是 O ( n log n ) \mathcal O(n\log n) ,不过常数优秀。

代码贰

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long int_;
inline int readint(){
    int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

struct Range{
	int l, r;
	bool operator < (const Range &t) const {
		return r < t.r; // 按照 r 排序
	}
};

const int MaxN = 500005;
const int Mod = 998244353;
int pow3[MaxN]; // 3^x % mod
Range ls[MaxN], rs[MaxN];
int tmp[MaxN<<1];
Range cnt[MaxN<<1];
int solve(Range a[],int n){
	pow3[0] = 1;
	for(int i=1; i<=n; ++i)
		pow3[i] = 3ll*pow3[i-1]%Mod;
	for(int i=0; i<n; ++i){
		ls[i].l = rs[i].l = i;
		ls[i].r = a[i+1].l;
		rs[i].r = a[i+1].r;
		tmp[i<<1] = ls[i].r;
		tmp[i<<1|1] = rs[i].r;
	}
	sort(ls,ls+n), sort(rs,rs+n);
	sort(tmp,tmp+(n<<1));
	int m = unique(tmp,tmp+n*2)-tmp;
	int now = 0; // 目前的 hash 值
	int pl = 0, pr = 0;
	for(int i=0; i<m; ++i){
		if(i > 0){
			cnt[i].l = tmp[i]-tmp[i-1];
			cnt[i].r = now; // 所需要求
		}
		while(pl < n && ls[pl].r == tmp[i]){
			now += pow3[ls[pl].l];
			now %= Mod, ++ pl;
		}
		while(pr < n && rs[pr].r == tmp[i]){
			now += Mod-pow3[rs[pr].l];
			now %= Mod, ++ pr;
		}
	}
	int ans = 0;
	sort(cnt+1,cnt+m);
	for(int i=1,j=1; i<m; i=j){
		int sum = 0;
		while(j < m && cnt[j].r == cnt[i].r)
			sum += cnt[j].l, ++ j; // [i,j) 内相同
		ans = max(ans,sum);
	}
// printf("ans = %d\n",ans);
	return ans;
}

Range x[MaxN], y[MaxN];
int main(){
	freopen("globe.in","r",stdin);
	freopen("globe.out","w",stdout);
	int n = readint(), X = readint(), Y = readint();
	for(int i=1; i<=n; ++i){
		x[i].l = readint(), y[i].l = readint();
		x[i].r = readint(), y[i].r = readint();
	}
	++ n; // 加入地图边缘
	x[n].l = 0, x[n].r = X;
	y[n].l = 0, y[n].r = Y;
	printf("%lld\n",1ll*solve(x,n)*solve(y,n));
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/108433406
今日推荐