CF#398(Div.2) 解题报告

本文解析了三道算法题目,包括物品摆放方案、最小等待时间和树的权值分割问题。通过详细阐述每道题目的题意、数据范围、解题思路及代码实现,帮助读者理解并掌握相关算法技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A

题意简述

有n个大小为1..n的物品,每一天会得到一个,物品必须由下而上按照从大到小的顺序摆放
每一天会将已有的物品尽量摆放,问这n天的摆放方案

数据范围

1n100000

题解

只有一个物品只有当比它大的所有物品都得到时才能摆放
模拟即可

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<ctime>
#include<cmath>
#include<map>
using namespace std;
#define N 100005

int n,x,now;
bool vis[N];

int main()
{
    scanf("%d",&n);
    now=n;
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&x);
        vis[x]=1;
        while (vis[now]) printf("%d ",now--);
        puts("");
    }
}

B

题意简述

接待处服务时间为0点之后ts~tf-1,每一个人需要的服务时间为t
有一些人在某一个时刻来并且排队
V也会在某一个时间来,如果这个时间也有别人来,他会排在这些人的后面
求V的最小等待时间

数据范围

0n100000,ts,tf,t1012

题解

首先判断服务是否有断层,如果有的话即在那个时刻来
如果没有断层,枚举V在哪一个时间点来,有价值的时间点只有n个时间点以及两个时间点之间的断点
预处理时间点ai来的人需要办理服务到什么时间
注意n=0需要特判
算是贪心吧

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<ctime>
#include<cmath>
#include<map>
using namespace std;
#define LL long long
#define N 100005

const LL inf=1e18;
int n,Max;
LL s,t,l;
LL now,a[N],ed[N],ans,ansp;

int main()
{
    scanf("%I64d%I64d%I64d",&s,&t,&l);--t;
    scanf("%d",&n);
    if (!n) {printf("%I64d\n",s);return 0;}
    for (int i=1;i<=n;++i) scanf("%I64d",&a[i]);
    now=s;Max=n;
    for (int i=1;i<=n;++i)
    {
        if (a[i]<=now) now+=l,ed[i]=now-1;
        else {printf("%I64d\n",now);return 0;}
        if (now>t-l+1) {Max=i;break;}
    }
    if (ed[Max]<t-l+1) {printf("%I64d\n",ed[Max]+1LL);return 0;}

    ans=inf;
    for (int i=Max-1;i>=1;--i)
        if (a[i]==a[i+1]) ed[i]=max(ed[i],ed[i+1]);

    if (a[1]>0)
    {
        now=s-(a[1]-1LL);
        if (now<ans) ans=now,ansp=a[1]-1LL;
    }
    for (int i=1;i<=Max;++i)
    {
        if (ed[i]<t-l+1)
        {
            now=ed[i]+1LL-a[i];
            if (now<ans) ans=now,ansp=a[i];
        }
        if (i==Max||a[i+1]-a[i]<=1LL) continue;
        else
        {
            now=ed[i]+1LL-(a[i+1]-1LL);
            if (now<ans) ans=now,ansp=a[i+1]-1LL;
        }
    }
    printf("%I64d\n",ansp);
    return 0;
}

C

题意简述

一棵有根树每一个节点有一个权值
需要将这棵树断开两条边变成三棵树,并满足三棵树的权值和相等
无解-1
注意:只能将某一个节点与其父亲相连的那条边断开

数据范围

3n106,100ti100

题解

首先总权值和被3整除
可以满足条件的只有两种情况
1、两棵子树权值和为sum3,并且互不包含
2、一棵子树权值和为2sum3,一棵子树权值和为sum3并且前者包含后者
dfs即可

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<ctime>
#include<cmath>
#include<map>
using namespace std;
#define N 1000005

int n,fa,root,sum,id;
int tot,point[N],nxt[N],v[N];
int size[N],ans[5];
bool pd,flag[N];

void add(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void findse(int x)
{
    for (int i=point[x];i;i=nxt[i])
    {
        if (size[v[i]]==sum/3)
        {
            ans[2]=v[i];
            printf("%d %d\n",ans[1],ans[2]);
            exit(0);
        }
        findse(v[i]);
    }
}
void findsy(int x)
{
    if (pd) return;
    if (size[x]==sum/3)
    {
        ans[++id]=x;
        pd=1;
        return;
    }
    for (int i=point[x];i;i=nxt[i])
        findsy(v[i]);
}
void dfs(int x)
{
    int cnt=0;
    for (int i=point[x];i;i=nxt[i])
    {
        dfs(v[i]);
        size[x]+=size[v[i]];
        if (flag[v[i]]) ++cnt;
    }
    if (cnt>=2)
    {
        id=0;
        for (int i=point[x];i;i=nxt[i])
            if (flag[v[i]])
            {
                pd=0;
                findsy(v[i]);
                if (id==2) break;
            }
        printf("%d %d\n",ans[1],ans[2]);
        exit(0);
    }
    if (x!=root&&(cnt||size[x]==sum/3)) flag[x]=1;
    if (x!=root&&size[x]==sum/3*2&&flag[x])
    {
        ans[1]=x;
        findse(x);
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
    {
        scanf("%d%d",&fa,&size[i]);
        sum+=size[i];
        if (fa) add(fa,i);
        else root=i;
    }
    if (sum%3!=0) {puts("-1");return 0;}
    dfs(root);
    if (ans[1]&&ans[2]) printf("%d %d\n",ans[1],ans[2]);
    else puts("-1");
}

一点总结。。。

感觉最近各种傻逼→_→
①认真读题!认真看数据范围!尤其是极限的情况(不光包括上界也包括下界)
②要认真想不合法的情况,加特判
③想清楚再写,非常麻烦的题不要慌,一点一点写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值