USACO2025 JAN Platinum 简要题解
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。
这时合法首先要满足只用头尾的操作所有元素 ,我们可以根据此求出头部操作次数的范围。
接着所有 的元素会对中间操作的位置提出限制,而每个点所有这样的时刻数之和是 的。
然后注意到每个限制是一个区间,所以对每个时刻判断所有区间是否并集为全集即可。
时间复杂度 ,常数极小,最大点大约 。
#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);
// }
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2024-02-03 THUWC2024 / WC2024 游记