JOI 2021 Final

链接

B. Snowball

显然每个雪球不可能在越过其他雪球获得雪。对于每个雪球求出其左右两边覆盖的长度,具体求法可以直接二分然后判断两端到达这个位置的时间,以此判断这个位置被谁覆盖。

复杂度 O(nlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200010
#define ll long long
using namespace std;
ll s[N],p[N],ft[N],gt[N],ans[N];
const ll inf=1e18;
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&p[i]);
    for(int i=1;i<=m;i++) scanf("%lld",&s[i]),s[i]+=s[i-1];
    for(int i=1;i<=m;i++) ft[i]=max(s[i],ft[i-1]),gt[i]=max(-s[i],gt[i-1]);
    p[0]=-inf,p[n+1]=inf;
    for(int i=0;i<=n;i++)
    {
        ll d=p[i+1]-p[i];
        if(d>=ft[m]+gt[m]){ans[i]+=ft[m],ans[i+1]+=gt[m];continue;}
        int l=1,r=m,res=1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(ft[mid]+gt[mid]>d) r=mid-1,res=mid;
            else l=mid+1;
        }
        if(gt[res]!=gt[res-1]) ans[i+1]+=d-ft[res],ans[i]+=ft[res];
        else ans[i+1]+=gt[res],ans[i]+=d-gt[res];
    }
    for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
    return 0;
}

C. Group Photo

由于身高是一个排列,容易发现最后形成的一定是若干 4321|654|987 这样若干连续下降段构成的上升序列。

考虑 dp。设 fi,j 表示将 1i 放置在前 i 个位置,最后一个是 j 的合法方案。通过 j 也可以知道 i 的位置。由于前 i1 个数已经确定,可以推出 i 的当前位置。对于当前 i 可以:移到 i1 的右边,或者单独开一条链。分别处理贡献即可。

复杂度 O(n2)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 5010
using namespace std;
int a[N],p[N],f[N],g[N][N];const int inf=1e9;
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),p[a[i]]=i;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) g[i][j]=g[i-1][j]+(a[i]<=j);
    for(int i=1;i<=n;i++) f[i]=inf;
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=i,s=0;j;j--)
            s+=(i-j)+(g[p[j]][n]-g[p[j]][i])-(g[p[j]][i]-g[p[j]][j]),f[i]=min(f[i],f[j-1]+s);
    printf("%d\n",f[n]);
    return 0;
}

D. Robot

由于可选颜色范围 [1,m],一定存在一种方案使得修改后的边与其他所有边颜色不同。假设现在在 u,要经过边 i,要么修改与 u 相连的边所有颜色为 ci 的边,要么修改当前边的颜色。

但是注意到我们无法记录当前点是从哪里来的,可能会出现重复修改某条边的情况。不妨对每个点的每个颜色新建一个点,走到这个点不需要代价,因为走到这个点表示接下来要走相同颜色的边,所以这条边会在走出去的时候计算贡献。这样就避免重复贡献。

跑最短路。复杂度 O(mlogn)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
#define N 400010
#define fi first
#define se second
using namespace std;
typedef long long ll;
map<int,vector<pair<int,int>>>g[N];
int nxt[N<<2],to[N<<2],head[N],cnt;ll w[N<<2];
void add(int u,int v,ll w0){nxt[++cnt]=head[u];to[cnt]=v;w[cnt]=w0;head[u]=cnt;}
ll dis[N];bool vis[N];priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<pair<ll,int>>>q;
ll dij(int s,int t)
{
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0,q.push({0,s});
    while(!q.empty())
    {
        int u=q.top().se;q.pop();
        if(vis[u]) continue;vis[u]=true;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];if(dis[v]<=dis[u]+w[i]) continue;
            dis[v]=dis[u]+w[i],q.push({dis[v],v});
        }
    }
    return dis[t];
}
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,c,p;scanf("%d%d%d%d",&u,&v,&c,&p);
        g[u][c].push_back({v,p}),g[v][c].push_back({u,p});
    }
    int tt=n;
    for(int u=1;u<=n;u++)
        for(auto [c,p]:g[u])
        {
            // cerr<<"in: "<<u<<" "<<c<<endl;
            ll res=0;for(auto a:p) res+=a.se;
            for(auto [v,w]:p) add(u,v,min((ll)w,res-w));
            if(p.size()<=1) continue;
            ++tt;
            for(auto [v,w]:p) add(v,tt,0),add(tt,v,res-w);
        }
    ll ans=dij(1,n);
    printf("%lld\n",ans>1e18?-1ll:ans);
    return 0;
}

E. Dungeon 3

posted @   Flying2018  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示