hdu6602 Longest Subarray (转化+线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6602

我们考虑固定右端点,考虑每种数字的合法左端点区间
第一种合法区间是该数字上次出现的位置以右到右端点,
第二种合法区间是从 \(1\) 到该数字向前出现第 \(k\) 次的位置

对于每种数字,将这些区间覆盖,也即加一,查询时在线段树上二分,找到最大值为 \(C\) 的最左端的位置即可

因为每个位置只有一个数字,所以在移动右区间时,只会对当前位置的数字的合法区间有影响,所以修改当前数字的覆盖区间即可

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;

const int maxn = 100010;

int n, c, k;
int a[maxn];
int la[maxn], prek[maxn][20], posi[maxn];

struct Node{
	int mx, add;
}t[maxn << 2];

int find(int x){
	int pos = x;
	int flag = 0;
	for(int j = 15 ; j >= 0 ; --j){
		if((k >> j) & 1 == 1){
			if(flag) pos = la[pos];
			pos = prek[pos][j];
			flag = 1;
		}
	}
	
	return pos;
} 

void pushup(int i){
	t[i].mx = max(t[i << 1].mx, t[i << 1 | 1].mx);
}

void build(int i, int l, int r){
	if(l == r){
		t[i].mx = 0;
		return;
	}
	int mid = (l + r) >> 1;
	build(i << 1, l, mid);
	build(i << 1 | 1, mid + 1, r);
	pushup(i);
}

void pushdown(int i){
	if(t[i].add){
		t[i << 1].add += t[i].add;
		t[i << 1 | 1].add += t[i].add;
		t[i << 1].mx += t[i].add;
		t[i << 1 | 1].mx += t[i].add;
		t[i].add = 0;
	} 
}

void modify(int i, int k, int x, int y, int l, int r){
	if(x > y) return;
	if(x <= l && r <= y){
		t[i].add += k;
		t[i].mx += k;;
		return;
	}
	
	pushdown(i);
	int mid = (l + r) >> 1;
	if(x <= mid) modify(i << 1, k, x, y, l, mid);
	if(y > mid) modify(i << 1 | 1, k, x, y, mid + 1, r); 
	pushup(i);
}

int query(int i, int x, int y, int l, int r){
	if(x <= l && r <= y){
		return t[i].mx;
	}
	pushdown(i);
	int mid = (l + r) >> 1;
	int res = 0;
	if(x <= mid) res = max(res, query(i << 1, x, y, l, mid));
	if(y > mid) res = max(res, query(i << 1 | 1, x, y, mid + 1 ,r));
	return res;
}

int qry(int i, int l, int r){
	if(l == r){
		return l;
	}
	int mid = (l + r) >> 1;
	if(query(1, l, mid, 1, n) == c){
		return qry(i, l, mid);
	} else if(query(1, mid + 1, r, 1, n) == c){
		return qry(i, mid + 1, r);
	} else{
		return 1000000007;
	}
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	while(scanf("%d%d%d", &n, &c, &k) != EOF){
		
		memset(t, 0, sizeof(t));
		memset(posi, 0, sizeof(posi));
		memset(la, 0, sizeof(la));
		memset(prek, 0, sizeof(prek));
		for(int i = 1 ; i <= n ; ++i) scanf("%d", &a[i]);
		
		for(int i = 1 ; i <= n ; ++i){
			la[i] = posi[a[i]];
			prek[i][0] = i;
			posi[a[i]] = i;
		}
		
		for(int j = 1 ; j <= 15 ; ++j){
			for(int i = 1 ; i <= n ; ++i)
			prek[i][j] = prek[la[prek[i][j - 1]]][j - 1];
		}
		
		int ans = 0;

		modify(1, c - 1, 1, 1, 1, n);
		int pos = find(1);
		modify(1, 1, 1, pos, 1, n);

		for(int i = 2 ; i <= n ; ++i){
			// 消除之前的区间覆盖 
			modify(1, -1, la[i] + 1, i - 1, 1, n); // 当前数字出现了, 删掉当前数字未出现的合法区间
			int pos = find(la[i]);
			modify(1, -1, 1, pos, 1, n);
			
			// 加上当前的区间覆盖 
			pos = find(i);
			modify(1, 1, 1, pos, 1, n);
			modify(1, c - 1, i, i, 1, n); // 在这个位置,c - 1个数字都未出现 

			ans = max(ans, i - qry(1, 1, n) + 1);
		}
		printf("%d\n", ans);
	}
	
	return 0;
}
posted @ 2020-12-21 15:21  Tartarus_li  阅读(76)  评论(0编辑  收藏  举报