JOI 2019 Final
链接
A | B | C | D | E |
---|---|---|---|---|
B. Exhibition
将画按美观度,画框按大小从大到小排序。显然画框越大越好,所以一定选取一个前缀。
直接从前往后贪心匹配可以放进第 个画框的画即可,不配不到就寄。复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
struct node{int s,v;}p[N];
int c[N];
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d",&p[i].s,&p[i].v);
for(int i=1;i<=m;i++) scanf("%d",&c[i]);
sort(c+1,c+m+1);
sort(p+1,p+n+1,[&](node x,node y){return x.v==y.v?x.s<y.s:x.v<y.v;});
for(int i=m,j=n;i;i--,j--)
{
while(j && c[i]<p[j].s) j--;
if(!j){printf("%d\n",m-i);return 0;}
}
printf("%d\n",m);
return 0;
}
C. Growing Vegetable is Fun 3
容易发现最优方案不会交换两个相同的数,所以每种字母的相对位置不会变化。考虑 dp,设 表示 个 R, 个 G, 个 Y,最后一个是 R/G/Y 的最小代价。
枚举下一个位置放什么。由于下一个的初始位置 是固定的,当前的位置可以根据 推出。故距离可以 计算。
总复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 410
using namespace std;
int p[3][N],c[3],f[150][210][N][3],s[3][N];char str[N];
void chkmin(int &x,int y){x=min(x,y);}
int calc(int x,int i,int j,int k){return abs(x+max(0,i-s[0][x])+max(0,j-s[1][x])+max(0,k-s[2][x])-(i+j+k+1));}
int main()
{
int n;scanf("%d%s",&n,str+1);
for(int i=1;i<=n;i++) if(str[i]=='R') p[0][++c[0]]=i,s[0][i]++;
else if(str[i]=='G') p[1][++c[1]]=i,s[1][i]++; else p[2][++c[2]]=i,s[2][i]++;
for(int i=1;i<=n;i++) for(int j=0;j<3;j++) s[j][i]+=s[j][i-1];
if(c[0]>c[1]) swap(p[0],p[1]),swap(c[0],c[1]),swap(s[0],s[1]);
if(c[0]>c[2]) swap(p[0],p[2]),swap(c[0],c[2]),swap(s[0],s[2]);
if(c[1]>c[2]) swap(p[1],p[2]),swap(c[1],c[2]),swap(s[1],s[2]);
memset(f,0x3f,sizeof(f));
f[0][0][0][0]=f[0][0][0][1]=f[0][0][0][2]=0;
cerr<<c[0]<<" "<<c[1]<<" "<<c[2]<<endl;
for(int i=0;i<=c[0];i++)
for(int j=0;j<=c[1];j++)
for(int k=0;k<=c[2];k++)
{
if(i<=c[0]) chkmin(f[i+1][j][k][0],min(f[i][j][k][1],f[i][j][k][2])+calc(p[0][i+1],i,j,k));
if(j<=c[1]) chkmin(f[i][j+1][k][1],min(f[i][j][k][0],f[i][j][k][2])+calc(p[1][j+1],i,j,k));
if(k<=c[2]) chkmin(f[i][j][k+1][2],min(f[i][j][k][0],f[i][j][k][1])+calc(p[2][k+1],i,j,k));
}
int ans=*min_element(f[c[0]][c[1]][c[2]],f[c[0]][c[1]][c[2]]+3);
printf("%d\n",ans>1e9?-1:ans);
return 0;
}
D. Coin Collecting
首先所有硬币都先塞到最近的合法位置,然后调整。
对每条边界计算被经过几次。显然横边在任意一次移动中只会被经过至多一次,不妨钦定横边永远在最后才会被经过,这样只需要枚举终止格子的时候计算有多少是从下方来的。对于竖向直接统计多余硬币数量即可。复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
using namespace std;
struct node{
int x,y;
}p[N];
int vis[N][2];
int main()
{
int n;scanf("%d",&n);
memset(vis,-1,sizeof(vis));
long long ans=0;
for(int i=0;i<n*2;i++)
{
int x,y;scanf("%d%d",&x,&y);
--x,--y;
if(x<0) ans+=-x,x=0;
if(x>=n) ans+=x-n+1,x=n-1;
if(y<0) ans+=-y,y=0;
if(y>1) ans+=y-1,y=1;
vis[x][y]++;
}
int v0=0,v1=0,s=0;
for(int i=0;i<n;i++)
{
v0+=vis[i][0],v1+=vis[i][1];
ans+=abs(v0+v1);
if(v0<0 && v1>0) s=min(-v0,v1),ans+=s,v0+=s,v1-=s;
if(v0>0 && v1<0) s=min(v0,-v1),ans+=s,v0-=s,v1+=s;
}
printf("%lld\n",ans);
return 0;
}
E. Unique Cities
显然独特的城市一定在 和离他最远的点对应路径上。根据直径的性质,这个点一定是直径的一点。
考虑以直径端点为根,统计最远点是该点的点,相当于统计其到根路径上所有合法点的颜色数。
从根开始 dfs。对于每个子树,设其子树内的最长路径长度为 ,那么其到根路径所有距离它 的位置都是不合法的。
如果当前往子树内最长链方向走,那么所有距离它 次长链的都是不合法的。否则所有距离它 最长链的都是不合法的显然 dfs 过程中不合法的一定仍然不合法,可以用一个栈维护合法点,每次下传的时候加入当前点。
用一个桶记录下每种颜色出现次数,可以 计算。
复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=200010;
int len[N],len2[N],son[N];vector<int>g[N];
int d1[N],d2[N],dep[N],w[N];
void dfs0(int u,int p,int d[]){d[u]=d[p]+1;for(int v:g[u]) if(v!=p) dfs0(v,u,d);}
void dfs1(int u,int p)
{
len[u]=0,len2[u]=-1,dep[u]=dep[p]+1,son[u]=0;
for(int v:g[u]) if(v!=p)
{
dfs1(v,u);
if(len[v]+1>len[u]) len2[u]=len[u],len[u]=len[v]+1,son[u]=v;
else if(len[v]+1>len2[u]) len2[u]=len[v]+1;
}
}
int col[N],ans[N],ton[N],tt[N],res,tp;
void add(int x){ton[++tp]=x,res+=!tt[w[x]],tt[w[x]]++;}
void pop(){tt[w[ton[tp]]]--,res-=!tt[w[ton[tp]]],--tp;}
void solve(int u,int p)
{
while(tp && dep[u]-dep[ton[tp]]<=len2[u]) pop();
add(u);
if(son[u]) solve(son[u],u);
while(tp && dep[u]-dep[ton[tp]]<=len[u]) pop();
for(int v:g[u]) if(v!=p && v!=son[u])
{
if(ton[tp]!=u) add(u);
solve(v,u);
}
if(ton[tp]==u) pop();
if(col[u]) ans[u]=res;
}
int main()
{
int n,m;scanf("%d%d",&n,&m);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
dfs0(1,0,d1);
int rt1=1;for(int i=2;i<=n;i++) if(d1[i]>d1[rt1]) rt1=i;
dfs0(rt1,0,d1);
int rt2=1;for(int i=2;i<=n;i++) if(d1[i]>d1[rt2]) rt2=i;
dfs0(rt2,0,d2);
for(int i=1;i<=n;i++) col[i]=d1[i]>d2[i];
dfs1(rt1,0),solve(rt1,0);
for(int i=1;i<=n;i++) col[i]=!col[i];
dfs1(rt2,0),solve(rt2,0);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
本文来自博客园,作者:Flying2018,转载请注明原文链接:https://www.cnblogs.com/Flying2018/p/JOI2019Final.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理