【HDU 6602】Longest Subarray

1.题目链接。题目大意,定义一个好的序列是这样的,序列中的每个数至少出现k次,找出给定序列中好的序列最长的哪个,输出长度即可。

2.这个题目开始写了一个分治T了,分治的思想大概就是:首先我们用出现次数小于k的数把区间划分,然后答案一定在这些区间里,然后继续做这样的操作分治下去,直到出现合法的区间,然后统计答案。但是这存在一个问题,就是会出现快速排序中的那种问题,快排中如果你的划分支点选的不好,很有可能会把区间划分为一个元素在左边,其他的在右边,就是说这个分治的效果很差,这个问题使用分治也会存在这个问题。正解是线段树,枚举右端点(左边也行),查找最近的符合条件的左端点。至于怎么查,用线段树维护一下区间信息,然后在线段树上二分即可。时间复杂度(n*log(n)).

#include<bits/stdc++.h>
using namespace std;

#define ml ((l+r)>>1)
#define mr (ml+1)
const int maxn = 1e5 + 5;
int mx[maxn << 1], lz[maxn << 1], ls[maxn << 1], rs[maxn << 1], cnt;

inline void maketrue(int& rt) {
	if (rt == -1) {
		rt = ++cnt;
		ls[rt] = rs[rt] = -1;
		lz[rt] = mx[rt] = 0;
	}
}

inline void push_down(int& rt) {
	maketrue(ls[rt]);
	maketrue(rs[rt]);
	lz[ls[rt]] += lz[rt];
	lz[rs[rt]] += lz[rt];
	mx[ls[rt]] += lz[rt];
	mx[rs[rt]] += lz[rt];
	lz[rt] = 0;
}

inline void push_up(int& rt) 
{
	mx[rt] = max(mx[ls[rt]], mx[rs[rt]]);
}

void update(int& rt, int l, int r, int ql, int qr, int d) 
{
	maketrue(rt);


	if (ql <= l && r <= qr) 
	{
		lz[rt] += d;
		mx[rt] += d;
		return;
	}
	if (lz[rt] != 0) push_down(rt);
	if (ml >= ql) update(ls[rt], l, ml, ql, qr, d);
	if (mr <= qr) update(rs[rt], mr, r, ql, qr, d);
	push_up(rt);
}

int query(int& rt, int l, int r, int ql, int qr, int d) {
	maketrue(rt);
	if (mx[rt] < d) return -1;
	if (l == r)return mx[rt] >= d ? l : -1;//leaf
	if (lz[rt] != 0) push_down(rt);
	if (ml >= ql && mx[ls[rt]] >= d) return query(ls[rt], l, ml, ql, qr, d);
	if (mr <= qr && mx[rs[rt]] >= d) return query(rs[rt], mr, r, ql, qr, d);
	return -1;
}
int main() 
{
	int n, c, k;
	while (~scanf("%d%d%d",&n,&c,&k)) 
	{
		
		vector<int> a(n);
		for (int i = 0; i < n; i++)scanf("%d", &a[i]);
		vector<queue<int>> q(c+1);
		cnt = -1;
		int ans = 0, rt = -1;
		for (int i = 0; i < n; i++)
		{
			if (q[a[i]].size() == k)
			{
				update(rt, 0, n - 1, q[a[i]].front() + 1, q[a[i]].back(), 1);
				q[a[i]].pop();
				q[a[i]].push(i);
				update(rt, 0, n - 1, q[a[i]].front() + 1, q[a[i]].back(), -1);
			}
			else 
			{
				if (!q[a[i]].empty()) update(rt, 0, n - 1, 0, q[a[i]].back(), 1);
				q[a[i]].push(i);
				if (q[a[i]].size() != k) update(rt, 0, n - 1, 0, q[a[i]].back(), -1);
				else update(rt, 0, n - 1, q[a[i]].front() + 1, q[a[i]].back(), -1);
			}
			int qry = query(rt, 0, n - 1, 0, i, 0);
			if (qry != -1) ans = max(ans, i - qry + 1);
		}
		cout << ans << endl;
		
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值