JOI 2020 Final
链接
A | B | C | D | E |
---|---|---|---|---|
\(\color{green}{\texttt{+}}\) | \(\color{green}{\texttt{+}}\) | \(\color{green}{\texttt{+}}\) | \(\color{green}{\texttt{+}}\) | \(\color{green}{\texttt{+}}\) |
A. Just Long Neckties
B. JJOOII 2
签到题。
C. Collecting Stamps 3
考虑 dp。用 \(f_{i,j,k}\) 表示往左取到了 \(i\),往右取到了 \(j\),取了 \(k\) 个的最短时间。转移到 \((i+1,j)\) 和 \((i,j+1)\) 即可。其中个数一维可以滚动。
复杂度 \(O(n^3)\)。空间 \(O(n^2)\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=210,inf=0x3f3f3f3f;
pair<int,int>L[N],R[N];int f[N][N][2],g[N][N][2];
int x[N],t[N];
void chkmin(int &x,int y){x=min(x,y);}
int main()
{
int n,m,mx=0;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&x[i]);
for(int i=1;i<=n;i++) scanf("%d",&t[i]),mx=max(mx,t[i]);
for(int i=1;i<=n;i++)
R[i]={x[i],t[i]},L[n-i+1]={m-x[i],t[i]};
memset(f,0x3f,sizeof(f)),memset(g,0x3f,sizeof(g));
f[0][0][0]=f[0][0][1]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<n;j++)
for(int k=0;j+k<n;k++)
{
if(L[j+1].first-L[j].first<=L[j+1].second-f[j][k][0])
chkmin(g[j+1][k][0],f[j][k][0]+(L[j+1].first-L[j].first));
else chkmin(f[j+1][k][0],f[j][k][0]+(L[j+1].first-L[j].first));
if(R[k+1].first-R[k].first<=R[k+1].second-f[j][k][1])
chkmin(g[j][k+1][1],f[j][k][1]+(R[k+1].first-R[k].first));
else chkmin(f[j][k+1][1],f[j][k][1]+(R[k+1].first-R[k].first));
if(R[k+1].first+L[j].first<=R[k+1].second-f[j][k][0])
chkmin(g[j][k+1][1],f[j][k][0]+(R[k+1].first+L[j].first));
else chkmin(f[j][k+1][1],f[j][k][0]+(R[k+1].first+L[j].first));
if(L[j+1].first+R[k].first<=L[j+1].second-f[j][k][1])
chkmin(g[j+1][k][0],f[j][k][1]+(R[k].first+L[j+1].first));
else chkmin(f[j+1][k][0],f[j][k][1]+(R[k].first+L[j+1].first));
}
bool hv=false;
for(int j=0;j<=n;j++)
for(int k=0;j+k<=n;k++) for(int _=0;_<=1;_++)
{
if(g[j][k][_]<inf) hv=true;
f[j][k][_]=g[j][k][_],g[j][k][_]=inf;
}
if(!hv){printf("%d\n",i-1);return 0;}
}
printf("%d\n",n);
return 0;
}
D. Olympic Bus
考虑对每条边,求出翻转之后 \(s\rightarrow t\) 最短路以及 \(t\rightarrow s\) 的最短路。
先求出 \(s\rightarrow t\) 最短路。容易发现,如果某条边 \(x\) 不在最短路径上,那么翻转这条边后至少是原最短路径长度,然后再考虑这条边即 \(s\rightarrow y\rightarrow x\rightarrow t\) 的长度。否则暴力重新计算。最短路径最多只有 \(n\) 条边,所以最多只会重新跑 \(O(n)\) 次。\(t\rightarrow s\) 同理。
单次最短路可以用非堆优化的 dijkstra,复杂度 \(O(n^3+nm)\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=210,inf=0x3f3f3f3f;
pair<int,int>L[N],R[N];int f[N][N][2],g[N][N][2];
int x[N],t[N];
void chkmin(int &x,int y){x=min(x,y);}
int main()
{
int n,m,mx=0;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&x[i]);
for(int i=1;i<=n;i++) scanf("%d",&t[i]),mx=max(mx,t[i]);
for(int i=1;i<=n;i++)
R[i]={x[i],t[i]},L[n-i+1]={m-x[i],t[i]};
memset(f,0x3f,sizeof(f)),memset(g,0x3f,sizeof(g));
f[0][0][0]=f[0][0][1]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<n;j++)
for(int k=0;j+k<n;k++)
{
if(L[j+1].first-L[j].first<=L[j+1].second-f[j][k][0])
chkmin(g[j+1][k][0],f[j][k][0]+(L[j+1].first-L[j].first));
else chkmin(f[j+1][k][0],f[j][k][0]+(L[j+1].first-L[j].first));
if(R[k+1].first-R[k].first<=R[k+1].second-f[j][k][1])
chkmin(g[j][k+1][1],f[j][k][1]+(R[k+1].first-R[k].first));
else chkmin(f[j][k+1][1],f[j][k][1]+(R[k+1].first-R[k].first));
if(R[k+1].first+L[j].first<=R[k+1].second-f[j][k][0])
chkmin(g[j][k+1][1],f[j][k][0]+(R[k+1].first+L[j].first));
else chkmin(f[j][k+1][1],f[j][k][0]+(R[k+1].first+L[j].first));
if(L[j+1].first+R[k].first<=L[j+1].second-f[j][k][1])
chkmin(g[j+1][k][0],f[j][k][1]+(R[k].first+L[j+1].first));
else chkmin(f[j+1][k][0],f[j][k][1]+(R[k].first+L[j+1].first));
}
bool hv=false;
for(int j=0;j<=n;j++)
for(int k=0;j+k<=n;k++) for(int _=0;_<=1;_++)
{
if(g[j][k][_]<inf) hv=true;
f[j][k][_]=g[j][k][_],g[j][k][_]=inf;
}
if(!hv){printf("%d\n",i-1);return 0;}
}
printf("%d\n",n);
return 0;
}
E. Fire
容易发现如果把 \(S_i(j)\) 的表打出来,每个位置占据的格子构成一个平行四边形。可以将其分解成 \(O(1)\) 个三角形。
对于每个三角形,对斜边和竖边分别计算贡献,每个贡献都可以用关于 \(x\) 的一次函数表示。用四个树状数组即可。
当有重复数字时,钦定前面的数字较大,复杂度 \(O(n\log n)\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 400010
using namespace std;
typedef long long ll;
int a[N],n;
struct fentree{
ll a[N];
void add(int x,ll v){x+=n+1;for(;x<=n*2+1;x+=x&-x) a[x]+=v;}
ll qry(int x){x+=n+1;ll v=0;for(;x;x-=x&-x) v+=a[x];return v;}
}l0,l1,r0,r1;
int pre[N],suf[N],ton[N],tp;
void add(int l,int r,ll v){l0.add(l,v),l1.add(l,-(l-1)*v),r0.add(r,-v),r1.add(r,r*v);}//[l,r] += v
ll qry(int t,int x){return l0.qry(x-t)*(x-t)+l1.qry(x-t)+r0.qry(x)*x+r1.qry(x);}
ll ans[N];
vector<pair<int,int>>g[N];vector<vector<int>>h[N];
int main()
{
int m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
while(tp && a[ton[tp]]<=a[i]) tp--;
pre[i]=ton[tp],ton[++tp]=i;
}
tp=0;
for(int i=n;i;i--)
{
while(tp && a[ton[tp]]<a[i]) tp--;
suf[i]=ton[tp],ton[++tp]=i;
}
for(int i=1;i<=n;i++)
{
int l=pre[i]?pre[i]:-n,r=suf[i]?suf[i]:n+1;
// printf("%d %d\n",l,r);
h[(r-1)-(l+1)+1].push_back({l+1,r-1,a[i]});
h[(i-1)-(l+1)+1].push_back({l+1,i-1,-a[i]});
h[(r-1)-(i+1)+1].push_back({i+1,r-1,-a[i]});
add(l+1,r-1,a[i]),add(l+1,i-1,-a[i]),add(i+1,r-1,-a[i]);
}
// for(int i=0;i<=n;i++,puts(""))
// {
// for(auto v:h[i]) add(v[0],v[1],-v[2]);
// for(int j=1;j<=n;j++) printf("%lld ",qry(i,j)-qry(i,j-1));
// }
for(int i=1,t,l,r;i<=m;i++) scanf("%d%d%d",&t,&l,&r),t=min(t,n-1),g[t].push_back({r,i}),g[t].push_back({l-1,-i});
for(int t=0;t<n;t++)
{
for(auto v:h[t]) add(v[0],v[1],-v[2]);
for(auto [u,id]:g[t])
if(id>0) ans[id]+=qry(t,u);
else ans[-id]-=qry(t,u);
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
本文来自博客园,作者:Flying2018,转载请注明原文链接:https://www.cnblogs.com/Flying2018/p/JOI2020Final.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)