Codeforces Round 906 (Div. 1) C2. Doremy‘s Drying Plan (Hard Version) (扫描线+线段树/ST表优化dp+背包)

题目

t(t<=1e4)组样例,每次给定n(n<=2e5)个城市,

这n个城市在数轴上,用[1,n]表示,

m(m<=2e5)天,第i天让区间[li,ri](1<=li<=ri<=n)的城市都下雨,

你有k(k<=10)次机会,可以选出k天来,让这k天不下雨

求最大的m天都不下雨的城市数

思路来源

Submission #230250470 - Codeforces SSRS代码(ST表优化dp)

题解

转化一下题意,即等价于数轴上点[1,n],m条线段,你可以撤掉最多k条线段,

问没有被线段覆盖的点的个数最大是多少

C1的时候k=2,所以可以分被覆盖了0次/1次/2次讨论

而C2的时候k=10,不太能讨论,所以考虑类似地从左到右的扫描线

dp[i][j]表示当前考虑到点i,已经用了j次撤销机会,点i必不被线段覆盖时,[1,i]的答案最大是多少

转移可以枚举前一个必不被线段覆盖的点位于哪里,

假设位于点x,然后就只需要考虑左端点位于[x+1,i]之间,且右端点在点i及右侧的线段,

假设这中间的线段有k2条,dp[i][j]就可以从dp[x][j-k2]转移而来,用了k2次机会,使得i不被覆盖

由于k<=10,所以只需考虑保留能覆盖点i,且左端点最靠右的10条线段

暴力做复杂度是O(n*k*n*k),考虑数据结构优化,

假设两个能覆盖i的相邻线段,左端点分别为L1,L2(L1<=L2)

那么[L1,L2)之间的x是可以放在一起考虑的,

因为用到的撤销机会是相同的,用线段树维护区间最大值即可

得到i的结果后,单点更新i的值即可,复杂度O(nlogn*k*k)

参考SSRS的代码后,发现这个log也是可以省掉的,

将其改为ST表的区间查询和单点更新即可,复杂度O(n*k*k+n*logn)

代码

// Problem: C2. Doremy's Drying Plan (Hard Version)
// Contest: Codeforces - Codeforces Round 906 (Div. 1)
// URL: https://codeforces.com/contest/1889/problem/C2
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<
using namespace std;
const int N=2e5+10,K=11,INF=0x3f3f3f3f;
struct segtree{
	int n;
	struct node{int l,r,v;}e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define v(p) e[p].v
	void up(int p){v(p)=max(v(p<<1),v(p<<1|1));}
	void bld(int p,int l,int r){
		l(p)=l;r(p)=r;v(p)=-INF;
		if(l==r){return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
	}
	void init(int _n){n=_n;bld(1,0,n);}
	void chg(int p,int x,int v){
		if(l(p)==r(p)){v(p)=max(v(p),v);return;}
		int mid=l(p)+r(p)>>1;
		chg(p<<1|(x>mid),x,v);
		up(p);
	}
	int amx(int p,int ql,int qr){
		if(ql>qr)return -INF;
		if(ql<=l(p)&&r(p)<=qr)return v(p);
		int mid=l(p)+r(p)>>1,res=-INF;
		if(ql<=mid)res=max(res,amx(p<<1,ql,qr));
		if(qr>mid)res=max(res,amx(p<<1|1,ql,qr));
		return res;
	}
}seg[K];
multisetq;
int t,n,m,k,l[N],r[N],tmp[N];
vectoradd[N],del[N];
int sol(){
	sci(n),sci(m),sci(k);
	q.clear();
	rep(i,0,k){
		seg[i].init(n);
		seg[i].chg(1,0,0);
	}
	rep(i,1,m){
		sci(l[i]),sci(r[i]);
		add[l[i]].pb(i);
		del[r[i]].pb(i);
	}
	q.insert(0);
	rep(i,1,n){
		for(auto &v:add[i]){
			q.insert(i);
		}
		auto it=prev(q.end());
		rep(j,0,k)tmp[j]=-INF;
		for(int k2=0;k2<=k;it=prev(it),k2++){
			int p=*it;
			rep(j,0,k-k2){
				int v=seg[j].amx(1,p,i);
				tmp[j+k2]=max(tmp[j+k2],v+1);
			}
			if(p==0)break;
		}
		rep(j,0,k)seg[j].chg(1,i,tmp[j]);
		for(auto &v:del[i]){
			q.erase(q.find(l[v]));
		}
		add[i].clear();
		del[i].clear();
	}
	int ans=0;
	rep(i,0,k){
		ans=max(ans,seg[i].amx(1,1,n));
	}
	return ans;
}
int main(){
	sci(t); // t=1
	while(t--){
		pte(sol());
	}
	return 0;
}

你可能感兴趣的:(#,dp优化,#,线段树/树状数组,线段树,扫描线,dp优化)