kmjp's blog

競技プログラミング参加記です

Codeforces #758 : E. The Cells on the Paper

これ系のテクは何度かあったな。
https://codeforces.com/contest/1608/problem/E

問題

2次元座標上に3N個の点がある。
各点は3色のいずれかで塗られており、各色N個の点がある。

以下を満たす最大のKを求めよ。

  • 各色K個ずつ頂点を残し、他の点を消す。各色ごとに、各色の点を覆う軸に平行な矩形領域を考える。その時、それらの領域は共有部分を持たない。

解法

2次元座標を3つの領域に分けることを考えると、分け方は以下の通り。

  • 3つ縦に並ぶ
  • 3つ横に並ぶ
  • 境界がT字状になるのが、回転を考慮して4通り。

それぞれのケース、それぞれの色の並びにおいて、2分探索の要領で最大何個までKを大きくできるか探索していくとよい。

int N;
int X[301010],Y[301010],C[301010],T[301010];

template<class V, int ME> class BIT {
public:
	V bit[1<<ME];
	V operator()(int e) {if(e<0) return 0;V s=0;e++;while(e) s+=bit[e-1],e-=e&-e; return s;}
	void add(int e,V v) { e++; while(e<=1<<ME) bit[e-1]+=v,e+=e&-e;}
};
BIT<int,19> bt[2];

vector<pair<int,int>> ev[301010];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N;
	vector<int> Xs,Ys;
	
	FOR(i,N) {
		cin>>X[i]>>Y[i]>>T[i];
		Xs.push_back(X[i]);
		Ys.push_back(Y[i]);
		T[i]--;
	}
	Xs.push_back(1<<30);
	Xs.push_back(-1<<30);
	Ys.push_back(1<<30);
	Ys.push_back(-1<<30);
	sort(ALL(Xs));
	sort(ALL(Ys));
	FOR(i,N) {
		X[i]=lower_bound(ALL(Xs),X[i])-Xs.begin();
		Y[i]=lower_bound(ALL(Ys),Y[i])-Ys.begin();
	}
	
	
	int ma=0;
	int step;
	FOR(step,4) {
		FOR(i,N) {
			x=N+1-Y[i];
			y=X[i];
			X[i]=x;
			Y[i]=y;
			assert(x>=1&&x<=N);
			assert(y>=1&&y<=N);
		}
		
		
		vector<int> V={0,1,2};
		do {
			vector<pair<int,int>> Vs;
			FOR(i,N+3) ev[i].clear();
			assert(bt[0](N+2)==0);
			assert(bt[1](N+2)==0);
			
			FOR(i,N) {
				C[i]=V[T[i]];
				Vs.push_back({Y[i],C[i]});
				ev[Y[i]].push_back({X[i],C[i]});
				if(C[i]!=0) bt[C[i]-1].add(X[i],1);
			}
			
			//縦に0,1,2;
			sort(ALL(Vs));
			int ret=0;
			for(i=20;i>=0;i--) if(3*(ret+(1<<i))<=N) {
				x=ret+(1<<i);
				y=0;
				int pre=-1;
				FORR2(a,c,Vs) {
					if(a>=pre&&y<x&&c==0) {
						y++;
						if(y==x) pre=a+1;
					}
					if(a>=pre&&y>=x&&y<2*x&&c==1) {
						y++;
						if(y==2*x) pre=a+1;
					}
					if(a>=pre&&y>=2*x&&y<3*x&&c==2) y++;
				}
				if(y==3*x) ret+=1<<i;
			}
			ma=max(ma,ret);
			
			ret=0;
			int n0=0;
			int fin=0;
			for(y=1;y<=N+2;y++) {
				FORR2(x,c,ev[y]) {
					if(c==0) n0++;
					if(c==1) bt[0].add(x,-1);
					if(c==2) bt[1].add(x,-1);
				}
				
				if(n0<=ma) continue;
				if(fin==0) {
					x=0;
					for(i=17;i>=0;i--) if((x+(1<<i))<=N) {
						int a=bt[0](x+(1<<i));
						int b=bt[1](N+2)-bt[1](x+(1<<i));
						if(a<=b) x+=1<<i;
					}
					int a=bt[0](x);
					int b=bt[1](N+2)-bt[1](x);
					int m1=min(a,b);
					ma=max(ma,min({n0,a,b}));
					
					a=bt[0](x+1);
					b=bt[1](N+2)-bt[1](x+1);
					int m2=min(a,b);
					ma=max(ma,min({n0,a,b}));
				}
			}
			
			
		} while(next_permutation(ALL(V)));
		
		
		
	}
	
	
	cout<<ma*3<<endl;
	
	
}

まとめ

3つの領域を考える問題、時々出るよね。