kmjp's blog

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

yukicoder : No.2808 Concentration

解法思いついても実装が大変なタイプの問題。
https://yukicoder.me/problems/no/2808

問題

N個の授業があり、開始時刻と終了時刻及び、1つの授業を全部受けると得られる知識量が与えられる。
起きている間授業を受けられるが、連続で起きていられるのは最大S分である。
また、一旦寝るとH分は寝ていないといけない。

最適な順で起きたり寝たりすると、得られる知識量の総和の最大値はどうなるか。

解法

区間加算・区間最大値を求められるSegTreeを用いる。
時刻について座標圧縮したうえで、時間順に走査し、前回起きた時刻に対する知識の総和の最大値をSegTreeで管理していく。

int N,S,H;
ll X[202020],Y[202020],Z[202020];
vector<int> ev[404040];

static ll const def=-(1LL<<60);
template<class V,int NV> class SegTree_3 {
public:
	vector<V> val, ma;
	SegTree_3(){
		int i;
		val.resize(NV*2,0); ma.resize(NV*2,0);
	};
	
	V getval(int x,int y,int l=0,int r=NV,int k=1) {
		if(r<=x || y<=l || y<=x) return def;
		if(x<=l && r<=y) return ma[k];
		return val[k]+max(getval(x,y,l,(l+r)/2,k*2),getval(x,y,(l+r)/2,r,k*2+1));
	}
	
	void update(int x,int y, V v,int l=0,int r=NV,int k=1) {
		if(l>=r||y<=x) return;
		if(x<=l && r<=y) {
			val[k]+=v;
			ma[k]+=v;
		}
		else if(l < y && x < r) {
			update(x,y,v,l,(l+r)/2,k*2);
			update(x,y,v,(l+r)/2,r,k*2+1);
			ma[k]=val[k]+max(ma[k*2],ma[k*2+1]);
		}
	}
};

ll dp[404040];
SegTree_3<ll,1<<20> st;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>S>>H;
	vector<ll> Xs;
	FOR(i,N) {
		cin>>X[i]>>Y[i]>>Z[i];
		Xs.push_back(X[i]);
		Xs.push_back(Y[i]+H);
	}
	sort(ALL(Xs));
	Xs.erase(unique(ALL(Xs)),Xs.end());
	FOR(i,N) {
		X[i]=lower_bound(ALL(Xs),X[i])-Xs.begin();
		Y[i]=lower_bound(ALL(Xs),Y[i]+H)-Xs.begin();
		ev[Y[i]].push_back(i);
	}
	j=0;
	ll ret=0;
	FOR(i,Xs.size()) if(i) {
		FORR(e,ev[i]) {
			st.update(0,X[e]+1,Z[e]);
		}
		while(Xs[j]+S+H<Xs[i]) j++;
		dp[i]=max(dp[i-1],st.getval(j,i));
		st.update(i,i+1,dp[i]);
		ret=max(ret,dp[i]);
	}
	cout<<ret<<endl;
	
	
}

まとめ

方針は難しくないが、実装で細々手間取ってしまった。