[GYM102979C]Colorful Squares
\[\text{newcommand 写的 LaTeX 起手式看不到就是了...}
\newcommand\bra[1]{\left({#1}\right)}
\newcommand\Bra[1]{\left\{{#1}\right\}}
\newcommand\dx[0]{\text{dx}}
\newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}}
\newcommand\down[2]{{#1}^{\underline{#2}}}
\newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor}
\newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil}
\newcommand\lcm[0]{\operatorname{lcm}}
\newcommand\set[1]{\left\{{#1}\right\}}
\]
壹、题目描述 ¶
贰、题解 ¶
先二分答案 \(s\) 转判定性问题。考虑扫描线,对于大小为 \(s\) 的正方形,先把 \(x=x_0\) 的所有点加入集合,再在 \(x=x_0+s+1\) 的时候把 \(x=x_0\) 加入的点从集合中删除。那么现在就是判断集合中是否存在一个 \(y\),使得 \([y-s,y]\) 里面有所有的 \(k\) 种颜色。
设有一个数据结构,其每个点 \(i\) 维护 \([i-s,i]\) 的颜色种数,假设我们加入点为 \(y_0\),那么它会增加该数据结构中的 \([y_0,y_0+s]\) 中所有点维护区间的颜色种数,该操作即区间 \(+1\),过,可能这其中已经有些点包含了该点的颜色 \(c_0\),故而,我们还需要对于每个颜色开一个 \(\tt multiset\) 维护该颜色所有 \(y\) 的位置,插入 \(y_0\) 时,找到前驱和后继 \(y_1,y_2\),那么我们最终修改的区间就是
\[\bra{\max\set{y_1+s+1,y_0},\min\set{y_2-1,y_0+s}}
\]
最后统计时,只需查看是否存在某个位置维护的值为 \(k\) 即可,这和区间最大值差不过。撤销操作,其实就是区间减 \(1\).
关于数据结构的选择,显然我们可以使用线段树做到。时间复杂度 \(\mathcal O(n\log x\log a)\).
叁、参考代码 ¶
# include <cstdio>
# include <algorithm>
# include <cstring>
# include <vector>
# include <set>
using namespace std;
// # define NDEBUG
# include <cassert>
namespace Elaina {
# define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
# define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
# define fi first
# define se second
# define mp(a, b) make_pair(a, b)
# define Endl putchar('\n')
# define mmset(a, b) memset(a, b, sizeof (a))
# define mmcpy(a, b) memcpy(a, b, sizeof (a))
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
template <class T> inline T fab(T x) { return x<0? -x: x; }
template <class T> inline void getmin(T& x, const T rhs) { x=min(x, rhs); }
template <class T> inline void getmax(T& x, const T rhs) { x=max(x, rhs); }
template <class T> inline T readin(T x) {
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
template <class T> inline void writc(T x, char s='\n') {
static int fwri_sta[1005], fwri_ed=0;
if(x<0) putchar('-'), x=-x;
do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
putchar(s);
}
} using namespace Elaina;
const int maxn=2e5;
const int maxx=2.5e5;
const int inf=0x3f3f3f3f;
int n, k;
vector<pii>v[maxx+5];
inline void input() {
n=readin(1), k=readin(1);
int x, y, c;
rep(i, 1, n) {
x=readin(1), y=readin(1), c=readin(1);
v[x].push_back({y, c});
}
}
namespace saya {
int mx[maxx<<2|2];
int tag[maxx<<2|2]; // modify tag
bool cls[maxx<<2|2]; // clear tag
# define ls (i<<1)
# define rs (i<<1|1)
# define mid ((l+r)>>1)
# define _lhs ls, l, mid
# define _rhs rs, mid+1, r
inline void clear(int i) {
mx[i]=tag[i]=0, cls[i]=1;
}
inline void add(int i, int val) {
mx[i]+=val, tag[i]+=val;
}
inline void pushdown(int i) {
if(!cls[i] && !tag[i]) return;
if(cls[i]) clear(ls), clear(rs), cls[i]=0;
if(tag[i]) add(ls, tag[i]), add(rs, tag[i]), tag[i]=0;
}
inline void pushup(int i) {
mx[i]=max(mx[ls], mx[rs]);
}
void modify(int L, int R, int val, int i=1, int l=1, int r=maxx) {
// if(i==1) printf("modify :> L == %d, R == %d, val == %d\n", L, R, val);
if(L>R || r<L || l>R) return;
if(L<=l && r<=R) return add(i, val);
pushdown(i);
if(L<=mid) modify(L, R, val, _lhs);
if(mid<R) modify(L, R, val, _rhs);
pushup(i);
}
# undef ls
# undef rs
# undef mid
# undef _lhs
# undef _rhs
}
multiset<int>c[maxn+5];
inline bool check(int s) {
/** initialization */
saya::clear(1);
rep(i, 1, k) {
c[i].clear();
c[i].insert(-inf), c[i].insert(inf);
}
rep(x, 1, maxx) {
/** add part */
for(auto [y, col]: v[x]) {
c[col].insert(y);
auto it=c[col].find(y);
auto pre=it, suf=it; --pre, ++suf;
saya::modify(max(*pre+s+1, y), min(*suf-1, y+s), 1);
}
/** remove part */
if(x-s-1>=1) {
for(auto [y, col]: v[x-s-1]) {
auto it=c[col].find(y);
assert(it!=c[col].end()); // it should have found
auto pre=it, suf=it; --pre, ++suf;
saya::modify(max(*pre+s+1, y), min(*suf-1, y+s), -1);
c[col].erase(it); // should erase the iterator
}
}
// printf("When x == %d, mx == %d\n", x, saya::mx[1]);
if(saya::mx[1]==k) return true;
}
return false;
}
signed main() {
input();
int l=0, r=maxx, mid, ans=-1;
while(l<=r) {
mid=(l+r)>>1;
// printf("Now l == %d, r == %d, mid == %d\n", l, r, mid);
if(check(mid)) ans=mid, r=mid-1;
else l=mid+1;
} writc(ans);
return 0;
}
肆、关键之处 ¶
好像之前做机器人那道傻逼树套树也用过这种思路,但是当时没调出来就忽略了这种思路,现在想想居然还挺巧妙,与其说是扫描线,我倒觉得更像是动态地维护一个队列以及其中的点的信息吧?不过说成是两根扫描线也挺形象。

浙公网安备 33010602011771号