ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

XM6138 分糖果(线段树)

2022-09-04 15:00:09  阅读:176  来源: 互联网

标签:ch log int XM6138 线段 mid res 糖果 sum


XM6138 分糖果

stO jc Orz

解题思路

很神奇的一题。

看到题目首先想到的就是二分答案。那么问题转化为了如何验证一个答案 \(x\)。

我们定义函数 \(f_{i,j}\) 表示考虑前 \(i\) 个小朋友,是否可以分成 \(j\) 段,使得每一段都满足和小于 \(x\)。这个函数很好转移:

\[f_{i,j}\leftarrow f_{k,j-1}\ \ sum_i-sum_k\leq x \]

其中 \(sum\) 表示前缀和。

目前转移是 \(O(n^3)\) 的,需要优化。那么我们以 \(sum_i\) 为关键字进行排序,构建线段树快速找出 \(k\) 这一维,使得 DP 复杂度来到了 \(O(n^2 \log n)\)。

但是这还远远不够,我们还有一个二分的 \(\log\)。我们通过观察可以发现,满足 \(f_{i,j}=1\) 的 \(j\) 一定实际一个连续的区间。那么我们在线段树上维护满足条件的 \(j\) 的区间即可。最终总时间复杂度为 \(O(n\log^2 n)\)。

现在问题就是,为什么 \(j\) 一定是连续的?在讨论后,我们把问题扔给了 IOI 金牌 jc,然后被秒了(

\(x\leq 0\) 时显然,那 \(x>0\) 时呢?我们可以假设 \(j=x,x+2\) 时成立,那么我们通过容斥发现,\(j=x+1\) 一定成立。至于如何容斥的,读者自证不难

代码

代码很简单。

//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;

int read() {
	char ch=getchar();
	int f=1,x=0;
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
char read_char() {
	char ch=getchar();
	while(!isalpha(ch)) {
		ch=getchar();
	}
	return ch;
}

const int MAXN=2e4+10; 

int n,k,m;
int a[MAXN],sum[MAXN],b[MAXN],idx[MAXN];

struct seg {
	int l,r;
}s[MAXN<<2];

seg pushup(seg lft,seg rgt) {
	seg ret=s[0];
	ret.l=min(lft.l,rgt.l);
	ret.r=max(lft.r,rgt.r);
	return ret;
}

void build(int l,int r,int p) {
	s[p].l=1e9;
	s[p].r=-1e9;
	if(l==r) {
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
}
void modify(int l,int r,int p,int x,int v1,int v2) {
	if(x<l||r<x) {
		return ;
	}
	if(l==r) {
		s[p].l=min(s[p].l,v1);
		s[p].r=max(s[p].r,v2);
		return ;
	}
	int mid=(l+r)>>1;
	modify(l,mid,p<<1,x,v1,v2);
	modify(mid+1,r,p<<1|1,x,v1,v2);
	s[p]=pushup(s[p<<1],s[p<<1|1]);
}
seg query(int l,int r,int p,int x,int y) {
	if(x<=l&&r<=y) {
		return s[p];
	}
	int mid=(l+r)>>1;
	if(y<=mid) {
		return query(l,mid,p<<1,x,y);
	}
	if(mid<x) {
		return query(mid+1,r,p<<1|1,x,y);
	}
	return pushup(query(l,mid,p<<1,x,y),query(mid+1,r,p<<1|1,x,y));
}

bool check(int x) {
	build(1,m,1);
	modify(1,m,1,idx[0],0,0);
	for(int i=1;i<n;i++) {
		int p=lower_bound(b+1,b+m+1,sum[i]-x)-b;
		if(p>m) {
			continue;
		}
		seg res=query(1,m,1,p,m);
		res.l++;res.r++;
		if(i==n-1) {
			if(res.l<=k&&k<=res.r) {
				return 1;
			}
			else {
				return 0;
			}
		}
		modify(1,m,1,idx[i],res.l,res.r);
	}
	return 0;
}

int main() {
	cin>>n>>k;
	for(int i=1;i<=n;i++) {
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
		b[i]=sum[i];
	}
	
	n++;
	b[n]=0;
	sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	for(int i=0;i<n;i++) {
		idx[i]=lower_bound(b+1,b+m+1,sum[i])-b;
	}
	
	int l=-1e9,r=1e9;
	while(l+1<r) {
		int mid=(l+r)>>1;
		if(check(mid)) {
			r=mid;
		}
		else {
			l=mid;
		}
	}
	if(check(l)) {
		cout<<l<<endl;
	}
	else {
		cout<<r<<endl;
	}
	
	return 0;
}

标签:ch,log,int,XM6138,线段,mid,res,糖果,sum
来源: https://www.cnblogs.com/huayucaiji/p/XM6138.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有