[jzoj]4883. 【NOIP2016提高A组集训第12场11.10】灵知的太阳信仰(线段树优化DP)

来源:互联网 发布:李炎恢php 百度网盘 编辑:程序博客网 时间:2024/06/11 10:48

https://jzoj.net/senior/#contest/show/2156/1

Problem

题目大意:

给定一段序列,要求把一段序列分成若干段,保证每一段里没有重复的原子,且一段的贡献值为这一段中最大的中子数.

求最小贡献和.

Solution

  • f[i]表示到第i个数的最小贡献和.

  • 显然有:f[i]=min{f[j]+s[j+1][i]}

  • 我们考虑用线段树去维护.

  • 首先,一个b[i],考虑到它能更新到的最大范围,是ki,其中k是第一个大于等于b[i]b[k].

  • 那么,把这一段贡献到线段树上,便是min{f[k..i]}+b[i].

  • 每次查询,直接查一段的最优解即可.

  • 后续:

    • 这里,注意要维护f的最小值,以及一段区间的最优答案,更新最优答案时,可以直接用一个区间的最小f,加上这段区间被更新的sum.
#include <iostream>#include <cstdio>#include <cstring>const int Maxn = 1e5 + 10, maxn = 1e4 + 10, Maxni = 2 * 1e5 + 10;using namespace std;int n,ans,maxni;int a[Maxn],b[Maxn],f[Maxn],g[Maxn],t[Maxn],bz[Maxn];int fmin[Maxni*4],fmax[Maxni*4],tree[Maxn*4],lzy[Maxn*4];void lazy(int x,int y) { tree[y] = lzy[x] + fmin[y], lzy[y] = lzy[x]; }void lbzy(int x,int y) { fmin[y] = lzy[y] = lzy[x]; }void find(int x,int st,int en,int l,int r,int p){    if ((l <= st) && (en <= r))    {        if (p==-1) ans = min(ans,tree[x]); else        if (p==-2) ans = max(ans,fmax[x]); else            tree[x] = p + fmin[x], lzy[x] = p;        return;    }    if (lzy[x] > 0) lazy(x,x << 1),lazy(x,(x << 1) + 1), lzy[x] = 0;    int mid = (st + en) >> 1;    if (mid>=l) find(x << 1,st,mid,l,r,p);    if (mid< r) find((x << 1) + 1,mid+1,en,l,r,p);    tree[x] = min(tree[x << 1],tree[(x << 1) + 1]);}void modify(int x,int st,int en,int p,int q){    if (st==en) fmin[x] = q; else    {        int mid = (st + en) >> 1;        if (mid >= p) modify(x << 1,st,mid,p,q); else modify((x << 1) + 1,mid+1,en,p,q);        fmin[x] = min(fmin[x << 1],fmin[(x << 1) + 1]);    }}void modifx(int x,int st,int en,int p,int q){    if (st==en) fmax[x] = q; else    {        int mid = (st + en) >> 1;        if (mid>=p) modifx(x << 1,st,mid,p,q); else modifx((x << 1) + 1,mid+1,en,p,q);        fmax[x] = max(fmax[x << 1],fmax[(x << 1) + 1]);    }}int main(){    freopen("array.in","r",stdin), freopen("array.out","w",stdout);    scanf("%d",&n);    for (int i = 1; i <= n; i++)        scanf("%d%d",&a[i],&b[i]), maxni = max(maxni,b[i]);    g[0] = 1, memset(fmin,7,sizeof(fmin)), memset(tree,7,sizeof(tree)), find(1,0,n,0,0,0), modify(1,0,n,0,0);    for (int i = 1; i <= n; i++)    {        g[i] = g[i-1], ans = 0, find(1,1,maxni,b[i],maxni,-2), t[i] = ans, modifx(1,1,maxni,b[i],i);        if (bz[a[i]]==0) bz[a[i]] = i; else g[i] = max(g[i],bz[a[i]] + 1), bz[a[i]] = i;    }    for (int i = 1; i <= n; i++)    {        ans = 1e9, find(1,0,n,t[i],i,b[i]); //把当前这一段"新"的 值给更新一下         ans = 1e9, find(1,0,n,g[i]-1,i-1,-1); //找到answer(g[i]-1,i-1)         f[i] = ans, modify(1,0,n,i,f[i]);  //单点修改f    }    printf("%d",f[n]);}
阅读全文
0 0
原创粉丝点击