13年杭州网络赛的一题, 同学推荐做的, 算是磨练一下自己的线段树, 最近在系统的学java, 确实很骚气, 就用java写了, 搞事情啊
看来线段树还是有点成果的, 虽然慢了一点, 但是1A
有大神这题40行dp, 太可怕了, 附个链接:DP递推计数(ORZ)
给定n个数找出Sum{mex(i, j)}
mex(i, j )指[i, j]区间未出现过得最小自然数值
首先可以求得mex(1, j)(1 <= j <= n)然后存mex[i]建线段树
预处理每个数的下一个出现位置
查询固定以第i个数位左端点的mex(i, j)后, 删左端点, 用区间更新把[1, i]内的mex清零
然后如果根节点的mex域大于a[i],找到最左边mex域大于a[i]的位置l, 如果这个位置在a[i]下一个出现未知的左边, 把这段[l, r - 1]的mex域更新为a[i].
java实现
//package adruill;
import java.util.*;
import static java.lang.Math.*;
import java.io.*;
public class Main{
final static int N = 200000 + 5;
static int[] mex = new int[N];//预处理mex
static int[] mx = new int[N << 2];//线段树mex域
static long[] sum = new long[N << 2];//sum域
static int[] lazy = new int[N << 2];//lazy
static int[] a = new int[N];
static int n;
public static void main(String[] args) throws IOException{
Scanner in = new Scanner(System.in);
while(in.hasNext()){
n = in.nextInt();
if(n == 0) break;
int[] vis = new int[N];
int[] next = new int[N];
int tmp = 0;
for(int i = 1; i <= n; ++i){
a[i] = in.nextInt();
if(a[i] > n) a[i] = n + 1;//对于大于n的都看作n + 1
vis[a[i]] = n + 1;
while(vis[tmp] != 0) ++tmp;//预处理mex
mex[i] = tmp;
}
for(int i = n; i > 0; --i){
next[i] = vis[a[i]];//逆向找a[i]的下一个出现位置
//ot("" + next[i]);
vis[a[i]] = i;
}
build(1, 1, n);//建树
long ans = 0;
for(int i = 1; i <= n; ++i){
ans += sum[1];
update(1, 1, n, 1, i, 0);
if(a[i] < mx[1]){//当前根mex域判断有没有l存在
int r = next[i];
int l = GetPos(1, 1, n, a[i]);//l
if(r > l)
update(1, 1, n, l, r - 1, a[i]);//这段区间更新
}
}
ot("" + ans);
}
in.close();
}
private static int GetPos(int rt, int l, int r, int v) {
if(l == r) return l;
int mid = (l + r) >> 1;
pushDown(rt, l, r);
if(mx[lch(rt)] > v) return GetPos(lch(rt), l, mid, v);//找最左边的位置
return GetPos(rch(rt), mid + 1, r, v);
}
private static void update(int rt, int l , int r, int ul, int ur, int v) {
if(ul <= l && ur >= r){
lazy[rt] = 1;
mx[rt] = v;
sum[rt] = (long)(r - l + 1) * v ;
return;
}
pushDown(rt, l, r);
int mid = (l + r) >> 1;
if(ul <= mid) update(lch(rt), l, mid, ul, ur, v);
if(ur > mid) update(rch(rt), mid + 1, r, ul, ur, v);
pushUp(rt);
}
private static void pushDown(int rt, int l, int r) {
if(lazy[rt] != 0){
int mid = (l + r) >> 1;
down(lch(rt), rt, mid - l + 1);
down(rch(rt), rt, r - mid);
lazy[rt] = 0;
}
}
private static void down(int s, int f, int len) {
lazy[s] = lazy[f];
mx[s] = mx[f];
sum[s] = (long)len * mx[f];
}
public static void build(int rt, int l, int r){
lazy[rt] = 0;
if(l == r){
sum[rt] = mex[l];
mx[rt] = mex[l];
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushUp(rt);
}
private static void pushUp(int rt) {
sum[rt] = sum[lch(rt)] + sum[rch(rt)];
mx[rt] = max(mx[lch(rt)], mx[rch(rt)]);
}
private static int rch(int rt) {
return lch(rt) | 1;
}
private static int lch(int rt) {
return rt << 1;
}
public static void ot(String s){
System.out.println(s);
//System.out.printf("%1$s %2$tB %2$td, %2$tY", "Due date : ", new Date());
}
}