zoukankan      html  css  js  c++  java
  • 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
    ", ans);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Hash大法
    最小表示法
    KMP算法题集
    分块总结
    2018 雅礼国庆集训
    二分图总结
    贪心总结
    Tire树总结(模板+例题)
    工具类文章合集
    网文胡乱汇总
  • 原文地址:https://www.cnblogs.com/tuchen/p/14168041.html
Copyright © 2011-2022 走看看