A
直接区间 dp 即可。
#include<bits/stdc++.h>
#define IL inline
#define reg register
#define N 755
#define oo (1<<29)
IL int read()
{reg int x=0,f=0; reg char ch=getchar();while(ch<'0'||ch>'9')f|=ch=='-',ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return f?-x:x;
}int n,a[N][N],s[N][N],f[N][N];IL void cmin(reg int &x,reg int y){x>y?x=y:0;}main()
{n=read();for(reg int i=1,j;i<=n;++i)for(j=1;j<i;++j)a[j][i]=read();for(reg int l=1,r;l<=n;++l)for(r=l;r<=n;++r)if(a[l][r]<0)s[l][r]=-a[l][r];for(reg int l=1,r;l<=n;++l)for(r=l;r<=n;++r)s[l][r]+=s[l][r-1];for(reg int r=1,l;r<=n;++r)for(l=r;l;--l)s[l][r]+=s[l+1][r];for(reg int l=n,r,i,w;l;--l)for(r=l+1;r<=n;++r){f[l][r]=oo;for(i=r;i>l;--i){w=f[l][i-1]+f[i][r];if(i!=l+1)w+=s[l+1][r]-s[l+1][i-1]-s[i][r];if(a[l][i]>0)w+=a[l][i];cmin(f[l][r],w);}}printf("%d\n",f[1][n]);
}
B
首先注意到只会有一个不在头尾的操作,这可以用调整法说明,如果有两个就可以往外移。
然后考察只操作头尾的答案,二分答案,确定和之后是容易判断的。
而存在不在头尾操作时的答案最多只会小一,所以我们只需要一次 check。
这时合法首先要满足只用头尾的操作所有元素 \(<n\),我们可以根据此求出头部操作次数的范围。
接着所有 \(>0\) 的元素会对中间操作的位置提出限制,而每个点所有这样的时刻数之和是 \(O(n\log n)\) 的。
然后注意到每个限制是一个区间,所以对每个时刻判断所有区间是否并集为全集即可。
时间复杂度 \(O(n\log^2n)\),常数极小,最大点 \(<\text{100ms}\)。
#include<bits/stdc++.h>
#define IL inline
#define reg register
#define N 100100
#define int long long
IL int read()
{reg int x=0,f=0; reg char ch=getchar();while(ch<'0'||ch>'9')f|=ch=='-',ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return f?-x:x;
}int n,a[N];IL void cmax(reg int &x,reg int y){x<y?x=y:0;}
IL void cmin(reg int &x,reg int y){x>y?x=y:0;}
IL int min(reg int x,reg int y){return x<y?x:y;}
IL int max(reg int x,reg int y){return x>y?x:y;}IL int ask(reg int x,reg int y)
{if(x<=0)return 0;return (x+y-1)/y;
}IL bool check1(reg int s) // s = x + y
{reg int x=0,y=0;for(reg int i=1,cx,cy;i<=n;++i){cx=i-1,cy=n-i;if(cx==cy){if(cx*s<a[i])return 0;continue;}if(cx<cy)cmax(y,ask(a[i]-cx*s,cy-cx));else cmax(x,ask(a[i]-cy*s,cx-cy));}return x+y<=s;
}IL int Abs(reg int x){return x<0?-x:x;}std::vector<int>vec[N*3];struct Range{int l,r;}p[N];
int m;IL bool check2(reg int s)
{static int b[N];for(reg int i=1;i<=n;++i)b[i]=a[i];reg int L=0,R=s;for(reg int i=1,cx,cy,l,r,k,c;i<=n;++i){cx=i-1,cy=n-i;if(cx==cy){if(cx*s+cx<a[i])return 0;continue;}if(cx>cy){k=a[i]-cy*s,c=cx-cy;l=ask(k-n,c),r=ask(k,c);cmax(L,l);}else{k=a[i]-cx*s,c=cy-cx;l=ask(k-n,c),r=ask(k,c);cmin(R,s-l); }}if(L>R)return 0;if(R-L>=n*3)return 1;for(reg int i=0;i<=R-L;++i)vec[i].clear();for(reg int i=1,cx,cy,l,r,k,c,j;i<=n;++i){cx=i-1,cy=n-i;if(cx==cy){for(j=0;j<=R-L;++j)vec[j].push_back(i);}else if(cx>cy){k=a[i]-cy*s,c=cx-cy;l=ask(k-n,c),r=ask(k,c);for(j=l;j<r;++j)if(L<=j&&j<=R)vec[j-L].push_back(i);}else{k=a[i]-cx*s,c=cy-cx;l=ask(k-n,c),r=ask(k,c);for(j=l;j<r;++j)if(L<=s-j&&s-j<=R)vec[s-j-L].push_back(i);}}for(reg int x=L,y,k,i;x<=R;++x){y=s-x,m=0;for(auto i:vec[x-L]){k=a[i]-(i-1)*x-(n-i)*y;if(k>0)p[++m]={max(1,i-k+1),min(n,i+k-1)};}std::sort(p+1,p+1+m,[&](auto a,auto b){return a.l<b.l;});for(i=1,k=0;i<=m;++i){if(p[i].l>k+1)return 1;cmax(k,p[i].r);}if(k<n)return 1;}return 0;
}IL void work()
{n=read();for(reg int i=1;i<=n;++i)a[i]=read();reg int l=0,r=2e18,mid;r=r/(n-1)+5;while(l<=r){mid=l+r>>1;if(check1(mid))r=mid-1;else l=mid+1;}if(l<=1){printf("%lld\n",l);return;}if(check2(l-2))printf("%lld\n",l-1);else printf("%lld\n",l);
}main()
{for(reg int t=read();t--;work());
}
C
对偶一下,然后写个暴力 dp。(可能不对偶也行)
根据暴力 dp 的转移方式,容易发现 dp 数组是凸的,于是直接用平衡树维护即可。
#include<bits/stdc++.h>
#define IL inline
#define reg register
#define int long long
#define N 2000200
#define oo (1ll<<60)
IL int read()
{reg int x=0,f=0; reg char ch=getchar();while(ch<'0'||ch>'9')f|=ch=='-',ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return f?-x:x;
}IL void cmax(reg int &x,reg int y){x<y?x=y:0;}const int B=1e6;
int n,w[N],c[N];
std::mt19937 rnd((unsigned long long)(new char));int ls[N],rs[N],sz[N],sum[N],rd[N],val[N],ct[N],rt,tot;
bool tag1[N];
int tag2[N];IL int newnode(reg int w,reg int c)
{rd[++tot]=rnd();sz[tot]=ct[tot]=c;sum[tot]=c*w,val[tot]=w;return tot;
}IL void pushup(reg int o)
{sz[o]=sz[ls[o]]+sz[rs[o]]+ct[o];sum[o]=sum[ls[o]]+sum[rs[o]]+val[o]*ct[o];
}IL void apply1(reg int o){if(o)std::swap(ls[o],rs[o]),val[o]*=-1,sum[o]*=-1,tag1[o]^=1,tag2[o]*=-1;}
IL void apply2(reg int o,reg int k){if(o)val[o]+=k,sum[o]+=k*sz[o],tag2[o]+=k;}IL void pushdown(reg int o)
{if(tag1[o])apply1(ls[o]),apply1(rs[o]),tag1[o]=0;if(tag2[o])apply2(ls[o],tag2[o]),apply2(rs[o],tag2[o]),tag2[o]=0;
}int merge(reg int x,reg int y)
{if(!x||!y)return x|y;if(rd[x]<rd[y]){pushdown(x),rs[x]=merge(rs[x],y);return pushup(x),x;}pushdown(y),ls[y]=merge(x,ls[y]);return pushup(y),y;
}void split1(reg int o,reg int k,reg int &x,reg int &y) // siz
{if(!o)return x=y=0,void();pushdown(o);if(sz[ls[o]]+ct[o]<=k)x=o,split1(rs[o],k-sz[ls[o]]-ct[o],rs[o],y);else if(sz[ls[o]]>=k)y=o,split1(ls[o],k,x,ls[o]);else{reg int nc=k-sz[ls[o]];reg int u=newnode(val[o],ct[o]-nc);ct[o]=nc,rs[u]=rs[o],rs[o]=ls[u]=0;pushup(x=o),pushup(y=u);return;}pushup(o);
}void split2(reg int o,reg int k,reg int &x,reg int &y) // val
{if(!o)return x=y=0,void();pushdown(o);if(val[o]>=k)x=o,split2(rs[o],k,rs[o],y);else y=o,split2(ls[o],k,x,ls[o]);pushup(o);
}int dfs(reg int o,reg int k)
{if(!o)return k;pushdown(o);k=dfs(ls[o],k);for(reg int i=0;i<ct[o];++i)k+=val[o],printf("%lld ",k);return dfs(rs[o],k);
}IL void print(reg int f0)
{printf("%lld ",f0);dfs(rt,f0);puts("");
}IL int max(reg int x,reg int y){return x>y?x:y;}main()
{n=read();for(reg int i=1;i<=n;++i)w[i]=read();for(reg int i=2;i<=n;++i)c[i]=read();rt=newnode(w[1],B);reg int f0=0,up=B;for(reg int i=2,x,y;i<=n;++i){if(up>c[i]){up=c[i];split1(rt,up,x,y);rt=x;}// print(f0);split2(rt,0,x,y);rt=merge(x,newnode(0,sz[y]+max(0,c[i]-up)));if(up<c[i])up=c[i];// print(f0);f0+=sum[rt],apply1(rt);// print(f0);apply2(rt,w[i]);// print(f0);split2(rt,0,x,y);// printf("[%lld, %lld]\n",f0,sum[x]);printf("%lld\n",f0+sum[x]);rt=merge(x,y);}// for(reg int i=2,j;i<=n;++i)// {// for(j=c[i]+1;j<=B;++j)f[j]=-oo;// for(j=1;j<=c[i];++j)cmax(f[j],f[j-1]);// std::reverse(f,f+c[i]+1);// for(j=0;j<=c[i];++j)f[j]+=w[i]*j;// reg int ans=-oo;// for(j=0;j<=c[i];++j)cmax(ans,f[j]);// printf("%lld\n",ans);// }
}