231023校内赛

T1 区间

piEsKdx.jpg

题解

很容易想到的一点是如果 \(k\) 足够大,那么把区间单独放到一个组里总比多个区间在一个组优

对于多个区间来说,区间之间如果两两不包含的话这道题会是比较好做的

就可以注意到如果一个大区间包含了一个小区间,那么大区间要么单独一组,要么和小区间同一组,这样会是比较优的选择

所以只需要对小区间做 dp ,求出 \(f_i\) 表示小区间形成了 \(i\) 组的最大贡献,然后把前 \(k−i\) 大的大区间每一个单独一组即可

小区间设 \(f_{i,j}\) 表示现在分了 \(i\) 组,考虑了前 \(j\) 个区间的最大贡献。每个 \(i\) 分别单调队列即可省掉一维并优化复杂度

到底是谁出的这么难想的第一题

#include<bits/stdc++.h>
#define N 5010
#define inf (1e9)
using namespace std;
struct node{
	int l,r;
}a[N],p[N];
int n,k,m,ans,tot,mx,len[N],f[N],g[N],top,last,stk[N];
bool cmp(node x,node y){
	return x.r==y.r?x.l>y.l:x.r<y.r;
}
bool great(int x,int y){
	return x>y;
}
void solve(){
	for(int i = 1;i<=n;i++) f[i] = -inf;
	for(int i = 1;i<=k;i++){
		top = 0;last = 1;g[0] = -inf;
		for(int j = 1;j<=n;j++){
			while(top&&f[stk[top]-1]+p[stk[top]].r<=f[j-1]+p[j].r) top--;
			stk[++top] = j;
			last = min(top,last);
			while(p[stk[last]].r<=p[j].l) last++;
			g[j] = f[stk[last]-1]+p[stk[last]].r-p[j].l;
		}
		for(int j = 0;j<=n;j++) f[j] = g[j];
		ans = max(ans,f[n]+len[k-i]);
	}
}
int main(){
	freopen("interval.in","r",stdin);
	freopen("interval.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>k;
	for(int i = 1;i<=n;i++)
		cin>>a[i].l>>a[i].r;
	sort(a+1,a+n+1,cmp);
	tot = mx = 0;
	for(int i = 1;i<=n;i++){
		if(!tot||mx<a[i].l){
			mx = a[i].l;
			p[++tot] = a[i];
		}else len[++m] = a[i].r-a[i].l;
	}
	sort(len+1,len+m+1,great);
	for(int i = 1;i<=m;i++)
		len[i]+=len[i-1];
	for(int i = m+1;i<=k;i++) 
		len[i] = -inf;
	n = tot;
	solve();
	cout<<ans;
	return 0;
}

T2妹子

piEysN6.jpg

题解

对于一个序列来说,它的最大值一定是单调不降的

所以很容易会往二分上想

那么对于一个决策点来说,如果左边的 \(a\) 序列的最大值比右边的 \(b\) 序列的最大值大

二分的时候那就应该把 \(mid\) 左移

反之,右移

对于这个二分,你可以在线段树上实现,做到 \(\mathcal O(n\log n)\) 的复杂度

也可以不在线段树上实现,只不过是 \(\mathcal O(n\log^2 n)\) 罢了

#include<bits/stdc++.h>
#define ll long long
#define inf (0x5fffffff)
#define N 500010
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
struct tree{
	int a[N];
	struct node{
		int l,r;
		ll mx,flag;
	}t[N<<2];
	void pushnow(int p,ll v){
		t[p].mx+=v;
		t[p].flag+=v;
	}
	void pushdown(int p){
		if(t[p].flag){
			pushnow(lc,t[p].flag);
			pushnow(rc,t[p].flag);
			t[p].flag = 0;
		}
	}
	void build(int p,int l,int r){
		t[p].l = l,t[p].r = r;
		t[p].mx = -inf;t[p].flag = 0;
		if(l==r){
			t[p].mx = a[l];
			return ;
		}
		int mid = (l+r)>>1;
		build(lc,l,mid);
		build(rc,mid+1,r);
		t[p].mx = max(t[lc].mx,t[rc].mx);
	}
	ll query(int p,int l,int r){
		if(t[p].l>=l&&t[p].r<=r)
			return t[p].mx;
		pushdown(p);
		ll ans = -inf,mid = (t[p].l+t[p].r)>>1;
		if(l<=mid) ans = max(ans,query(lc,l,r));
		if(r>mid&&ans<t[rc].mx) ans = max(ans,query(rc,l,r));
		return ans;
	}
	void add(int p,int l,int r,int v){
		if(t[p].l>=l&&t[p].r<=r){
			pushnow(p,v);
			return ;
		}
		pushdown(p);
		int mid = (t[p].l+t[p].r)>>1;
		if(l<=mid) add(lc,l,r,v);
		if(r>mid) add(rc,l,r,v);
		t[p].mx = max(t[lc].mx,t[rc].mx);
	}
}T1,T2;
int n,m;
int main(){
	freopen("girl.in","r",stdin);
	freopen("girl.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i = 1;i<=n;i++)
		cin>>T1.a[i];
	for(int i = 1;i<=n;i++)
		cin>>T2.a[i];
	T1.build(1,1,n);T2.build(1,1,n);
	for(int i = 1;i<=m;i++){
		char opt;int ql,qr,v;
		cin>>opt>>ql>>qr>>v;
		if(opt=='A') T1.add(1,ql,qr,v);
		else T2.add(1,ql,qr,v);
		cin>>ql>>qr;
		int l = ql-1,r = qr;
		while(l<r){
			int mid = (l+r)>>1;
			if(T1.query(1,ql,mid)>T2.query(1,mid+1,qr)) r = mid;
			else l = mid+1;
		}
		cout<<min(T1.query(1,ql,r),T2.query(1,r,qr))<<"\n";
	}
	return 0;
}

T3 数对

piE2Hds.jpg

题解

对于这个题有一个结论就是按 \(a+b\) 排序一定是最优的

因为可以分类讨论一下对于 \(a,b\) 大小关系的四种情况,就能得出这个结论

自己手写一下,挺简单的,我就不说了我是真的懒

然后其实就是 \(dp\) 的事了

\(f_{i,j}\) 表示前 \(i\) 个数中,已选的数的 \(\max(a) = j\) ,最多选了多少个数

转移时只需要更新选了第 \(i\) 个数的情况,分别考虑 \(j\le \min(a_i,b_i)\)\(a_i< j \le b_i\)

用线段树维护即可做到 \(\mathcal O(n \log n)\)

#include<bits/stdc++.h>
#define N 100010
#define ll long long 
using namespace std;
struct node{
	int a,b;ll p;
}a[N];
struct tree{
	#define lc (p<<1)
	#define rc ((p<<1)|1) 
	struct node{
		int l,r;
		ll mx,flag;
	}t[N<<4];
	void pushnow(int p,ll v){
		t[p].mx+=v;
		t[p].flag+=v;
	}
	void pushdown(int p){
		if(t[p].flag){
			pushnow(lc,t[p].flag);
			pushnow(rc,t[p].flag);
			t[p].flag = 0;
		}
	}
	void build(int p,int l,int r){
		t[p].l = l,t[p].r = r;
		if(l==r) return ;
		int mid = (l+r)>>1;
		build(lc,l,mid);
		build(rc,mid+1,r);
		t[p].mx = max(t[lc].mx,t[rc].mx);
	}
	ll query(int p,int l,int r){
		if(t[p].l>=l&&t[p].r<=r)
			return t[p].mx;
		pushdown(p);
		ll ans = 0,mid = (t[p].l+t[p].r)>>1;
		if(l<=mid) ans = max(ans,query(lc,l,r));
		if(r>mid&&ans<t[rc].mx) ans = max(ans,query(rc,l,r));
		return ans;
	}
	void add(int p,int l,int r,ll v){
		if(t[p].l>=l&&t[p].r<=r){
			pushnow(p,v);
			return ;
		}
		pushdown(p);
		int mid = (t[p].l+t[p].r)>>1;
		if(l<=mid) add(lc,l,r,v);
		if(r>mid) add(rc,l,r,v);
		t[p].mx = max(t[lc].mx,t[rc].mx);
	}
	void change(int p,int x,ll v){
		if(t[p].l==t[p].r&&t[p].l==x){
			t[p].mx = max(t[p].mx,v);
			return ;
		}
		pushdown(p);
		int mid = (t[p].l+t[p].r)>>1;
		if(x<=mid) change(lc,x,v);
		else change(rc,x,v);
		t[p].mx = max(t[lc].mx,t[rc].mx);
	}
}T;
int n,m,w[N<<2];
bool cmp(node x,node y){
	return x.a+x.b<y.a+y.b;
}
int main(){
	freopen("pair.in","r",stdin);
	freopen("pair.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i = 1;i<=n;i++){
		cin>>a[i].a>>a[i].b>>a[i].p;
		w[++m] = a[i].a;
		w[++m] = a[i].b;
	}
	sort(w+1,w+m+1);
	m = unique(w+1,w+m+1)-w-1;
	for(int i = 1;i<=n;i++){
		a[i].a = lower_bound(w+1,w+m+1,a[i].a)-w;
		a[i].b = lower_bound(w+1,w+m+1,a[i].b)-w;
	}
	sort(a+1,a+n+1,cmp);
	T.build(1,1,m);
	for(int i = 1;i<=n;i++){
		int tmp = min(a[i].a,a[i].b);
		ll j = T.query(1,1,tmp)+a[i].p;
		T.change(1,a[i].a,j);
		if(tmp<a[i].b) T.add(1,tmp+1,a[i].b,a[i].p);
	}
	cout<<T.query(1,1,m);
	return 0;
}

T4

摆了,不写

posted @ 2023-10-24 19:17  cztq  阅读(14)  评论(0编辑  收藏  举报