福建省队集训 20180711

福建省队集训 20180711

sequence

没有这个单调不降的条件就是个傻逼斜率优化,不过这样的题也不会出现在省选题里...

其实就是把一维的条件升级成二维,cdq分治应该可以做,不过n是1e6

既然没有1e5的部分分这应该就是正解...吧?

然后这里的斜率优化是 >和递减查询,维护上凸壳和单调队列。

没有大样例差差差差评

注意要单独归并然后存起来,因为递归顺序不一样,而且用vector会T

题解复杂度还真是nlog,なんだよ、結構当たんじゃねぇか

#include<bits/stdc++.h>
#define int ll 
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define ll long long
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i) 
using namespace std;
inline int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
} 
const int N = 2000201;
int n;
ll a[N],top;
int q[N],id[N];
ll f[N];
int cmp(int aa,int bb){
	return a[aa]==a[bb]?aa<bb:a[aa]<a[bb];
}
#define pii pair<int,int>
#define pb push_back
#define fi first
#define se second
#define mp make_pair
int vec[26][N]; 
inline double Y(int i){
	return 1.0*f[i]-1.0*(i*i+i)/2.0;
} 
inline double slope(int i,int j){
	return (Y(i)-Y(j))/(1.0*(i-j));
}
ll calc(int now,int pre){
	return f[pre]-1ll*(now-pre-1)*(now-pre)/2ll+a[now];
} 
inline void solve(int dep,int l,int r){
	if(l==r) return void();
	int mid=(l+r)>>1;
	solve(dep+1,l,mid);
	top=0;
/*	FOR(i,l,mid){
		int now=id[i];
		while(top>1&&slope(q[top-1],q[top])<slope(q[top],i)) top--;
		q[++top]=now;
	}*/
	int h=1;
	FOR(i,l,r){
		int now=vec[dep][i];
		if(now<=mid){
			now=id[now];
			while(top>h&&slope(q[top-1],q[top])<slope(q[top],now)) top--;
			q[++top]=now;
		}else{
			now=id[now];
			while(h<top&&slope(q[h],q[h+1])>=-now) h++;
			if(h<=top) f[now]=max(f[now],calc(now,q[h]));
		}
	}
	solve(dep+1,mid+1,r);
}
void merge(int dep,int l,int r){
	if(l==r) return vec[dep][l]=l,void();
	int mid=(l+r)>>1;
	merge(dep+1,l,mid);merge(dep+1,mid+1,r);int tl=l,tr=mid+1;
	FOR(i,l,r) vec[dep][i]=(tl<=mid&&(id[vec[dep+1][tl]]<id[vec[dep+1][tr]]||tr>r))?vec[dep+1][tl++]:vec[dep+1][tr++];
}
int lnk[N];
signed main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	n=read();
	FOR(i,1,n){
		a[i]=read();
		id[i]=i;
		f[i]=-1e18;	
	}
	sort(id+1,id+n+1,cmp);
	merge(1,0,n);
	solve(1,0,n);
	ll ans=-1e18;
	FOR(i,0,n){
		ans=max(ans,f[i]-1ll*(n-i)*(n-i+1)/2ll);
	}
	printf("%lld",ans);
	return 0;
}

rope

只想到枚举两种颜色然后全部先染色...
假设绳子上只有两种颜色,那么能折到长度为 2 当且仅当除了头尾以外,每 个极长颜色相同段长度都为偶数。

也就是说,除了第一段以外,每一段的开头的 下标是同奇偶的。

那么对于一种颜色 a,我们先枚举奇偶,再枚举另一种颜色 b。我们首先要 将所有其它颜色染成 a 或 b,代价为 n-x[a]-x[b] (x[i]表示 i 的个数),接着如果某 个位置奇偶性不对并且恰好是 a 和 b 相邻,那么需要额外 1 点代价。

那么我们可以不用枚举 b,只要枚举 a 的每一段,将某个颜色的代价+1,最 后查询最小值,再还原即可。

时间复杂度 O(n)

posted @ 2020-06-08 17:23  lcyfrog  阅读(105)  评论(0编辑  收藏  举报