题目
数据范围与提示
对于前
的数据,
。
对于前
的数据,
。
对于另
的数据,
。
对于
的数据,
。
思路壹
不难发现,“矩形”内的格子分成了四类, 是否成立、 是否成立。
所以 上的限制是独立的。问题转化为,对于 个区间,将其中一些设置为绿色,另外的为红色,格子位于所有绿色区间内,且不位于红色区间内,则可产生贡献。最大化之。
我的想法是,枚举一个格子,这个格子是最终的可用格子中最靠右的一个。那么显然,区间有三类:
- 在该格子右边的:涂成红色即可。无影响。
- 在该格子左边的:涂成红色。要减去所有红色区间的并集。
- 包含该格子的:涂成绿色。期望答案为这种区间的交集。
而这三种都可以动态维护。并集用线段树实现。该格子显然会是某个区间的右端点(考虑其为绿色)或左端点(考虑其为红色)。复杂度 。但是过不了。常数大。
代码壹
#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;
}
思路贰
枚举格子之后可以不用直接统计。而是转为求出区间的染色方案。可以哈希。
对于每一种染色方案取最大值即可。复杂度仍然是 ,不过常数优秀。
代码贰
#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;
}