• 【洛谷6564】[POI2007] 堆积木KLO(树状数组优化DP)


    点此看题面

    • 给定一个长度为(n)的序列,你可以删去其中若干元素,要求最大化(sum_{i=1}^{n'}[a_i=i])
    • (nle10^5,a_ile10^6)

    树状数组优化(DP)

    发现每个位置(i)只有被弄到第(a_i)个位置才能产生贡献,也就是说需要满足在前(i-1)个元素中删去了恰好(i-a_i)个。

    (f_j)表示把原序列中的位置(j)弄到了第(a_j)个位置上能满足条件的最大个数,考虑(j)能转移到(i)的条件:

    [egin{cases} j<i,\ a_j<a_i,\ j-a_jle i-a_i end{cases} ]

    发现满足了后两个条件一定能满足第一个,因此这其实是个二维偏序。

    (a_i)为下标开一个树状数组,然后按照(i-a_i)从小到大枚举转移即可。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define V 1000000
    using namespace std;
    int n;vector<int> p[N+5];struct TreeArray
    {
    	int a[V+5];I void U(RI x,CI v) {W(x<=V&&a[x]<v) a[x]=v,x+=x&-x;}//单点取max
    	I int Q(RI x,RI t=0) {W(x) t=max(t,a[x]),x-=x&-x;return t;}//前缀求最大值
    }T;
    int main()
    {
    	RI i,x;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),x<=i&&(p[i-x].push_back(x),0);//对于i-a[i]开桶
    	vector<int>::iterator it;for(i=0;i<=V;++i) for(it=p[i].begin();it!=p[i].end();++it) T.U(*it,T.Q(*it-1)+1);//树状数组优化DP
    	return printf("%d
    ",T.Q(V)),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    判断大小写数字个数,取交集和并集
    软件工程总结
    正则表达式(邮箱)
    今天距离你生日的天数
    字符数量,查回文
    解决一个表单中的两个或者多个按钮提交到不同的页面中问题
    jsp前台输入框不输入值,后台怎么取出整型?
    第十次作业
    CMD命令行
    Kali渗透安卓手机
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6564.html
Copyright © 2020-2023  润新知