題解 CF888E 【Maximum Subsequence】
阿新 • • 發佈:2020-09-21
題意簡述
見翻譯
題解
記錄一下犯下的一個 nt 錯誤。
首先我們有一個顯然的 DFS 暴力,每次兩種決策,選或不選,所以時間複雜度為 \(\Theta(2^{n})\)。
\(n\) 的範圍是 35,是過不了的,我們可以考慮折半搜尋。
關於折半搜尋可以看看 我的折半搜尋小計。
暴力搜出 \([1,\lfloor\frac{n}{2}\rfloor],[\lfloor\frac{n}{2}\rfloor+1,n]\) 的所有答案,記錄到兩個 vector 裡面。
這一部分的時間複雜度是 \(\Theta(2^{\lfloor\frac{n}{2}\rfloor})\)。
考慮合併貢獻。
先考慮一個暴力合併貢獻的方法。
我們記第一次搜尋搜出來的答案序列為 \(A_{1}\),同理有 \(A_{2}\)。
這裡的兩個答案序列都是在模 \(m\) 意義下的。
那麼對於每一個 \(A_{1,i}\),我們都可以暴力在 \(A_{2}\) 中尋找兩者相加模 \(m\) 的最大值。
那麼我們可以分類討論了,因為序列在模 \(m\) 意義下,所以我們對於每一個 \(A_{1,i}\) 找到的 \(A_{2,j}\) 使得 \((A_{1,i}+A_{2,j})\bmod m\) 最大,都只有兩種情況。
一種是 \(A_{2,j}\) 在 \(A_{2}\) 中值域範圍在 \([0,m-A_{1,i}-1]\) 的所有值中最大,一種是在 \(A_{2}\)
所以我們在這兩種情況中取最大即可。
由於我不理解二分做法,所以我用的是動態開點值域線段樹。
(flag:動態開點不加引用就【】)
#include<bits/stdc++.h> using namespace std; const int N=35+5; const int H=99999999; int n,m,tot=1,root=1,ans,a[N]; struct Tree { int ls,rs,val; } nodes[(1<<(N>>1))<<3]; vector<int> vec[2]; void dfs(int x,int cur,int lim) { if(x>lim) { if(lim==(n>>1)) vec[0].push_back(cur); else vec[1].push_back(cur); return ; } dfs(x+1,(cur+a[x])%m,lim); dfs(x+1,cur,lim); } void ins(int &p,int l,int r,int x) { if(p==0) p=++tot; if(l==r) { nodes[p].val=l; return ; } int mid=(l+r)>>1; if(mid>=x) ins(nodes[p].ls,l,mid,x); else ins(nodes[p].rs,mid+1,r,x); nodes[p].val=max(nodes[nodes[p].ls].val,nodes[nodes[p].rs].val); } int find(int p,int l,int r,int x,int y) { if(l>y||r<x) return 0; if(l>=x&&r<=y) return nodes[p].val; int mid=(l+r)>>1,ret=0; if(mid>=x) ret=max(ret,find(nodes[p].ls,l,mid,x,y)); if(mid<y) ret=max(ret,find(nodes[p].rs,mid+1,r,x,y)); return ret; } void output(int p) { if(nodes[p].ls==0&&nodes[p].rs==0) { printf("%d ",nodes[p].val); return ; } output(nodes[p].ls); output(nodes[p].rs); } signed main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); dfs(1,0,n>>1); dfs((n>>1)+1,0,n); sort(vec[0].begin(),vec[0].end()); sort(vec[1].begin(),vec[1].end()); for(auto x:vec[1]) ins(root,0,m-1,x); for(auto x:vec[0]) ans=max(ans,max(x+find(1,0,m-1,0,m-x-1),(x+find(1,0,m-1,0,m*2-x-1))%m)); printf("%d\n",ans); return 0; }