*2019 Multi-University Training Contest 2:Longest Subarray(线段树+思维)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6602
题目大意:
题解:如果右端点固定,每一种元素可行左端点是两段连续的区间(个数 > = k >= k >=k = 0 = 0 =0),扫描右端点,用线段树维护区间最大值来维护每一个元素左端点的可行区间,在每种元素左端点的可行区间做区间 + 1。更新答案时查找 值为 C的下标最小值(值为C表示C种元素在这个位置都可行),在线段树上搜索加剪枝,由于每次查找的区间都是[1,i],剪枝后每一次查询的复杂度仍为 n l o g n nlogn nlogn。移动右端点时只会改变新纳入的元素的可行区间,每一次移动右端点维护复杂度为 n l o g n nlogn nlogn。注意每种元素不要重复维护已经维护过的区间。

#include<bits/stdc++.h>
using namespace std;
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
const int maxn = 1e5 + 10;
int val[maxn << 2],laz[maxn << 2],p[maxn << 2];
int n,c,k;
vector<int> g[maxn];
void up(int rt) {
	val[rt] = max(val[rt << 1],val[rt << 1 | 1]);
}
void down(int rt) {
	if(!laz[rt]) return;
	val[rt << 1] += laz[rt];
	laz[rt << 1] += laz[rt];
	val[rt << 1 | 1] += laz[rt];
	laz[rt << 1 | 1] += laz[rt];
	laz[rt] = 0; 
}
void build(int rt,int l,int r) {
	val[rt] = laz[rt] = 0;
	if(l == r) {
		p[l] = rt;
		return ;
	}
	int mid = l + r >> 1;
	build(lson);build(rson);
}
void upd(int L,int R,int v,int rt,int l,int r) {
	if(L > R) return ;
	if(L <= l && r <= R) {
		laz[rt] += v;
		val[rt] += v;
		return ;
	}
	down(rt);
	int mid = l + r >> 1;
	if(L <= mid) upd(L,R,v,lson);
	if(mid + 1 <= R) upd(L,R,v,rson);
	up(rt);
}
int qry(int L,int R,int v,int rt,int l,int r) {
	if(L > R) return 0;
	if(val[rt] != c || (l < l && r > R)) return 0;
	if(l == r) return l;
	int mid = l + r >> 1;
	down(rt);
	if(L <= mid) {
		int t = qry(L,R,v,lson);
		if(t) return t;
	}
	if(mid + 1 <= R) return qry(L,R,v,rson);
}
int main() {
	//freopen("in.txt","r",stdin);
	while(~scanf("%d%d%d",&n,&c,&k)) {
		int x;
		build(1,1,n);
		for(int i = 1; i <= c; i++) {
			g[i].clear();
			g[i].push_back(0);
		}
		int ans = 0;
		for(int i = 1; i <= n; i++) {
			scanf("%d",&x);
			upd(i,i,c - 1,1,1,n);
			upd(g[x].back() + 1,i - 1,-1,1,1,n);
			g[x].push_back(i);
			int p = (int)g[x].size() - k - 1;
			if(p >= 0) 			
				upd(g[x][p] + 1,g[x][p + 1],1,1,1,n);
			int t = qry(1,i,c,1,1,n);
			if(t)
				ans = max(ans,i - t + 1);
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值