T1
从高到底按位确定答案
A=1时f[i]表示前i个数合法的划分至少需要分出几段,时间复杂度$O(n^2log(ans))$
A>1时f[i][j]表示前i个数划分为j段是否可能合法,转移显然,时间复杂度$O(n^3log(ans)/32)$
#include<cstdio> #include<cstring> #include<bitset> typedef long long i64; int n,A,B; int f[2007]; i64 s[2007],ans=0; std::bitset<207>d[207]; int main(){ scanf("%d%d%d",&n,&A,&B); for(int i=1;i<=n;++i)scanf("%lld",s+i),s[i]+=s[i-1]; if(A==1){ for(int x=50;x>=0;--x){ i64 v=ans|((1ll<<x)-1); for(int i=1;i<=n;++i){ f[i]=0x3f3f3f3f; for(int j=0;j<i;++j)if(((s[i]-s[j])|v)==v&&f[j]+1<f[i])f[i]=f[j]+1; } if(f[n]>B)ans|=1ll<<x; } }else{ d[0][0]=1; for(int x=50;x>=0;--x){ i64 v=ans|((1ll<<x)-1); for(int i=1;i<=n;++i){ d[i].reset(); for(int j=0;j<i;++j)if(((s[i]-s[j])|v)==v) d[i]|=d[j]<<1; } bool dd=0; for(int i=A;i<=B;++i)dd|=d[n][i]; if(!dd)ans|=1ll<<x; } } printf("%lld\n",ans); return 0; }
T2
对每个不同的(p[i],b[i]%p[i]),新建一些点表示这个i可以走到的位置,每个位置和每个i也建出对应的点
最后可以转为01边权最短路
可以证明时空复杂度均为$O(n^\frac 3 2)$
#include<bits/stdc++.h> const int inf=0x3f3f3f3f,C=300; int n,m,b[30007],p[30007],B; struct edge{ int to; edge*nx; }*ep=0,*mep=0; edge*e0[30007*C]; int l[30007*C]; bool is[30007*C]; void ae(int a,int b){ if(ep==mep)ep=new edge[10000],mep=ep+10000; *ep=(edge){b,e0[a]}; e0[a]=ep++; } int idp; struct node{int w,l;void upd1(int);}; std::deque<node>q; std::map<int,int>mp; void node::upd1(int u){ if(::l[u]>l+1)q.push_back((node){u,::l[u]=l+1}); } int main(){ scanf("%d%d",&n,&m); for(int i=0;i<m;++i)scanf("%d%d",b+i,p+i); idp=n+m; for(int i=0;i<m;++i){ ae(b[i],n+i); ae(n+i,b[i]); int x=p[i],y=b[i]%x; int&z=mp[x<<15|y]; if(!z){ z=idp; int a; for(a=y;a+x<n;a+=x){ is[idp]=1; ae(idp++,a); } ae(idp++,a); } ae(n+i,z+(b[i]-y)/x); } std::fill(l,l+idp+1,inf); q.push_back((node){n,l[n]=0}); while(!q.empty()){ node w=q.front();q.pop_front(); if(w.l!=l[w.w])continue; if(w.w==n+1)return printf("%d\n",w.l),0; for(edge*i=e0[w.w];i;i=i->nx){ int u=i->to; if(l[u]>w.l)q.push_front((node){u,l[u]=w.l}); } if(is[w.w-1])w.upd1(w.w-1); if(is[w.w])w.upd1(w.w+1); } return puts("-1"),0; }
T3
对同侧的直接计算贡献,否则考虑不同侧的
K=1,中位数位置最优
K=2,可以证明按$S_i+T_i$排序后划分成左右两部分后按K=1的情况处理,能找到最优解
带插入的中位数可以用对顶堆维护
时间复杂度$O(nlogn)$
#include<bits/stdc++.h> typedef long long i64; int k,n; char s1[2],s2[2]; int p1,p2,xs[200007],xp=0; i64 ans=0; int abs(int x){return x>0?x:-x;} struct pos{ int a,b; bool operator<(pos w)const{return a+b<w.a+w.b;} }ps[100007]; int pp=0; i64 ss1=0,ss2=0,f[100007]; std::priority_queue<int>q1; std::priority_queue<int,std::vector<int>,std::greater<int> >q2; void init(){ q1=std::priority_queue<int>(); q2=std::priority_queue<int,std::vector<int>,std::greater<int> >(); ss1=ss2=0; } void ins(int x){ if(q1.size()&&x<=q1.top()){ q1.push(x),ss1+=x; if(q1.size()>q2.size()+1){ int x=q1.top();q1.pop(); ss1-=x;ss2+=x; q2.push(x); } }else{ q2.push(x),ss2+=x; if(q2.size()>q1.size()+1){ int x=q2.top();q2.pop(); ss2-=x;ss1+=x; q1.push(x); } } } void mins(i64&a,i64 b){if(a>b)a=b;} i64 cal(){ return ss2-ss1; } int main(){ scanf("%d%d",&k,&n); if(k==1){ for(int t=0;t<n;++t){ scanf("%s%d%s%d",s1,&p1,s2,&p2); if(s1[0]==s2[0])ans+=abs(p2-p1); else ++ans,xs[xp++]=p1,xs[xp++]=p2; } if(xp){ std::nth_element(xs,xs+xp/2,xs+xp); int x=xs[xp/2]; for(int a=0;a<xp;++a)ans+=abs(xs[a]-x); } }else{ for(int t=0;t<n;++t){ scanf("%s%d%s%d",s1,&p1,s2,&p2); if(s1[0]==s2[0])ans+=abs(p2-p1); else ++ans,ps[pp++]=(pos){p1,p2}; } std::sort(ps,ps+pp); if(pp){ init(); for(int a=0;a<pp;++a){ ins(ps[a].a);ins(ps[a].b); f[a]=cal(); } i64 mx=f[pp-1]; init(); for(int a=pp-1;a;--a){ ins(ps[a].a);ins(ps[a].b); mins(mx,cal()+f[a-1]); } ans+=mx; } } printf("%lld\n",ans); return 0; }