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;
}