题解 P2943 【[USACO09MAR]清理Cleaning Up】

_虹_

2019-12-30 15:56:03

Solution

这里是:**菜的真实**的O(nsqrtnlogn)做法 发现每个划分内部显然不能超过sqrtn种食物,否则还不如一个一个分开优。 所以对于i点,我们可以枚举i点为划分的右端点时的食物种类数x,显然x相同的源状态j,必然是连续的一段。 我们维护以i为划分右端点时,食物种类数量在哪些点发生变化,枚举前sqrtn段转移即可。 对于食物种类数相同的一段,转移取min,可以用线段树维护。 用了set,需要吸氧。 现在luogu氧气好像炸了,所以手开了O2。。。 ~~编译需要c++11~~ ```cpp #pragma GCC optimize(2) #include <iostream> #include <set> #include <cmath> using namespace std; const int kmaxn=40000+5; typedef long long ll; const ll inf=1e15; int lst[kmaxn]; int a[kmaxn]; int n,m; struct node{ int l,r; ll v=inf; }; node T[kmaxn<<2]; #define LS (ls(p)) #define RS (rs(p)) #define LEAF (T[p].l==T[p].r) #define TARGET (T[p].l==l&&T[p].r==r) #define UPD upd(p) #define CALMID int mid=((T[p].l+T[p].r)>>1) inline int ls(int i){return i<<1;} inline int rs(int i){return ls(i)|1;} inline void upd(int p) { if(LEAF)return; T[p].v=min(T[LS].v,T[RS].v); } void build(int l,int r,int p){ T[p].l=l;T[p].r=r; if(LEAF)return; CALMID; build(l,mid,LS); build(mid+1,r,RS); } ll qry(int l,int r,int p) { if(TARGET) return T[p].v; CALMID; if(r<=mid)return qry(l,r,LS); else if(l>mid)return qry(l,r,RS); else{ return min(qry(l,mid,LS), qry(mid+1,r,RS)); } } void ins(int k,ll v,int p) { if(LEAF){ T[p].v=v; return; } CALMID; if(k<=mid)ins(k,v,LS); else ins(k,v,RS); UPD; } int srt; set<int,greater<int> > st; void solve(int p) { int cnt=1; auto itr=st.begin(); int ls=p; ll ans=p*p; ++itr; while(cnt<=srt && itr!=st.end()) { ans=min(ans,qry((*itr),ls,1)+cnt*cnt); ls=*itr; ++cnt; ++itr; } ins(p,ans,1); } int main() { ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;++i) cin>>a[i]; build(0,n,1); ins(0,0,1); srt=sqrt(n)+1; st.insert(0); for(int i=1;i<=n;++i) { if(lst[a[i]]) st.erase(lst[a[i]]); lst[a[i]]=i; st.insert(i); solve(i); } cout<<qry(n,n,1)<<endl; return 0; } ```