把过去和现在的笔记记录也搬了过来,也算是给以后留个念想吧,想想一开始打acm就是图一乐,后来发现这游戏还挺上头的,也算是度过了一段电竞生涯(xs)
早些时候的笔记写的好中二,连我自己看着都羞耻。
不过,就喜欢这种羞耻的感觉。
收录的题目大部分是个人认为质量不错的题目,以DP为主,非DP的题目都用※进行了标识。
当然,有些题解思路本身也是源自其他人的,不过除非特殊标注,否则都是用的自己的代码。
题目大意:
CF 1668D Optimal Partition
题目大意:一个数组(5e5),其中每个子段的价值为:当子段和为正则为子段长度,为负则为子段长度的相反数,为0则为0。现将其划分为任意个子段,需要求得价值的最大值并输出最大价值。
解:
一眼dp,转移依靠区间,5e5的复杂度不允许去枚举区间的割点,于是想到分离参数去单调队列,但是由于区间的转移是和转移的子段正负有关的,很难去直接用单调队列分离。
题目需要进一步的结论,事实上通过贪心我们知道,如果全是正数那么怎么分都可以,而如果有负数的话,我们尽量用正数把它拉上来使其价值从-1变为1,但是如果实在没法拉上来的负数,我们更加倾向于让他单独一个划分,使其造成的影响最小,避免其将原本的正数拉成负数。
所以在进行区间转移的时候,我们只需要考虑正区间的转移,也就是dp[i]=dp[k]+val(k+1,i)时,sum(k+1,i)一定为正,而另一种转移则是直接dp[i]=dp[i-1]+a[i]/abs(a[i]),考虑负数的情况。
但是即便如此,我们依然不知道对于i来说,哪些k能够进行转移,这时候用到权值线段树解决这个问题。
将原数组的前缀和求出,并按照前缀和去从小到大排序,记录下每一个数在前缀和序中的位置,这样很容易发现,在前缀和序中排在后面的减去前面的(并且原本在数组中就出现在前面的)肯定区间和大于0,那么对于每个数,我只要维护比它位置更小的数里面分参后最大的数,这个过程可以通过权值线段树实现。
各种意义上非常nb的题目,前后用到了贪心、单调队列dp、权值线段树、前缀和相减的思想,实在是2100之鉴。
代码
代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <utility> #include <vector> #include <istream> #include <map> #include <cmath> #include <stack> #include <set> #include <cstring> #include <string> #include <fstream> #define ll long long #define maxn 500005 #define mdl 998244353 #define clr(a,n) for(int i=0;i<n;i++)a[i]=0 #define cfast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define pll pair<ll,ll> #define pii pair<int,int> #define inc(i,a,n) for(int i=a;i<n;i++) #define vset(a,n,m) for(ll i=0;i<n;i++)a[i]=m; #define endl '\n' using namespace std; ll n,tree[maxn],a[maxn],qzh[maxn],pos[maxn],dp[maxn]; struct cpr { ll qzh, pos; friend bool operator<(cpr c1, cpr c2) { if (c1.qzh == c2.qzh)return c1.pos > c2.pos; return c1.qzh < c2.qzh; } }cp[maxn]; ll lowbit(ll num) { return num & -num; } void add(ll s, ll num) { for (ll i = s; i <= n+2; i += lowbit(i)) tree[i]=max(tree[i],num); } ll ask(ll s) { ll ans = -1e18; for (ll i = s; i >= 1; i -= lowbit(i)) ans=max(ans,tree[i]); return ans; } int main() { cfast; int t; cin >> t; while (t--) { cin >> n; inc(i, 1, n + 2)tree[i] = -1e18; qzh[1]=a[1] = 0; inc(i, 2, n+2) { cin >> a[i]; qzh[i] = qzh[i - 1] + a[i]; cp[i].qzh = qzh[i]; cp[i].pos = i; } cp[1].qzh = 0; cp[1].pos = 1; sort(cp+1, cp + n + 2); inc(i, 1, n + 2) { pos[cp[i].pos] = i; } inc(i, 1, n + 2) { dp[i] = dp[i - 1] + a[i] / max(1ll, abs(a[i])); ll tmp = ask(pos[i]); dp[i] = max(dp[i], tmp + i); add(pos[i], dp[i] - i); } cout << dp[n+1] << endl; } }
|
Copyright Notice: 此文章版权归EmiteInna所有,多看看吧。