Contest_5 0612 By lhq

本文解析了学军中学NOIP2013提高组原创模拟题day1的三道题目,分别介绍了“装果子”、“零件加工”和“种树”的算法思路与实现代码,涉及二分查找、贪心算法及差分约束系统。

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

题目来源:学军中学NOIP2013提高组原创模拟题day1
https://wenku.baidu.com/view/c766b6b7915f804d2a16c141.html?from=search

Solution

1.装果子 关键词:二分
二分V,然后每次O(n)判断一遍。如果可以装进全部的果子就二分左区间,如果不行就二分右区间。
开long long是必然的。
时间复杂度:O(nlog(2,10^14))

2.零件加工 关键词:贪心
按照t/s升序排序然后计算,比较大小的时候注意交叉相乘。
详细证明见HK’blog:http://blog.youkuaiyun.com/yuyaohekai/article/details/73182025
对于最后20%的数据相乘时可以使用二进制乘法以避免高精度
时间复杂度:O(nlog2n)

3.种树 关键词:差分约束系统 or 贪心
解法一:
开一个s数组记录前缀和。
根据题意我们可以得到3个约束条件:
s[r]-s[l-1]≥c,①
s[i]≥s[i-1],②
s[i]-s[i-1]≤k,③
根据①得s[l]-s[r]≤-c,在r和l-1之间连一条权值为-c的边。
根据②得s[i-1]-s[i]≤0,在i和i-1之间连一条权值为0的边。
根据③在i-1和i之间连一条权值为k的边。
50分算法:Bellman-Ford。
时间复杂度:O(nm)
70分算法:SPFA。
时间复杂度:O(km)
100分算法:
观察到最大可能需要连150w条边,因此我们要考虑有些边是否需要连。
我们可以只根据条件①计算,每次更新后O(n)检查是否满足条件②和③,如果不满足就修改,这样只用连50w条边,可以过全部数据。
Tip:事实上SPFA可以过100%

解法二: 贪心
题目中要求要种树种得少,就要使一棵树给多个区间使用,这样,尽量在重叠区间种树即可,而重叠位置一定是区间尾部。处理问题时,先按所有区间的结束位置排序,若结束位置相同,则按开始位置从大到小排序。之后依次处理每个区间,先在第一个区间尾部种满足要求的树,对下一个区间,看差多少棵就在该区间尾部种多少。
【算法步骤】:
1.先快排
2.对每个区间依次处理
a.从前到后扫描这个区间,统计点的个数;
b.若点的个数超过了要求的点数,则continue;
c.从该区间后向前扫描,添加覆盖点。
3.输出ans
Tip:这种方法只适用于小数据,对于大数据我们可以用树状数组加快求和,即过程a,还可以用并查集的方法来加速种树过程c,当有一段区间的树种满时把它与前一个区间合并就行了。

总结:

T1水过,T2没想到贪心,T3排序打错。。
T2的证明中我们有了发现:那就是比较之后的两种选择哪种更优,而不是拿出一种来算,有点差分约束的思想

CODE

T1

#include<bits/stdc++.h>
using namespace std;
#define ll long long

ll a[100100],n,m;

ll read()
{
    ll w=0,ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') w=w*10+ch-48,ch=getchar();
    return w;   
}

bool check(ll v)
{
    ll num=1,bag=v;
    for (ll i=1;i<=n;i++)
        if (bag>=a[i]) bag-=a[i];
        else if (num<m) num++,bag=v-a[i];
        else return false;
    return true;    
}

ll binary_answer(ll l,ll r)
{
    ll ans;
    while (l<=r)
    {
        ll mid=(l+r)>>1;
        if (check(mid)) ans=mid,r=mid-1;
        else l=mid+1;       
    }
    return ans; 
}

int main()
{
    n=read(); m=read();
    for (ll i=1;i<=n;i++)
        a[i]=read();
    ll min_val=0,max_val=0;
    for (ll i=1;i<=n;i++)
        min_val=max(min_val,a[i]),max_val+=a[i];
    ll ans=binary_answer(min_val,max_val);
    printf("%lld",ans);
    return 0;
}

T2

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 100010
ll n,m,tot,ans;

struct node 
{
    ll t,s;
}a[MAXN];
bool cmp(node a,node b)
{
    return a.t*b.s<a.s*b.t;
}

ll read()
{
    ll w=0,ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') w=w*10+ch-48,ch=getchar();
    return w;   
}

ll mul(ll a,ll b)
{
    ll ans=0;
    while (b)
    {
        if (b&1) ans=(ans+a)%m;
        a=(a+a)%m;
        b=b>>1;     
    }
    return ans; 
}

int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++)
        a[i].t=read(),a[i].s=read();
    sort(a+1,a+n+1,cmp);
    for (int i=1;i<=n;i++)
    {
        ans=(ans+mul(tot,a[i].s))%m;
        tot=(tot+a[i].t)%m;
    }
    cout<<ans<<endl;
    return 0;
}

T3 差分约束

#include<bits/stdc++.h>
using namespace std;
const int MAXN=500010,MAXM=1500010;
int Head[MAXN],Next[MAXM],dis[MAXN],n,m,tot;
int que[MAXN*2];
bool vis[MAXN];

struct Edge
{
    int v,w;
    Edge():v(0),w(0){};
    Edge(int _v,int _w):v(_v),w(_w){};
}E[MAXM];

int read()
{
    int w=0,ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') w=w*10+ch-48,ch=getchar();
    return w;   
}

void AddEdge(int u,int v,int w)
{
    tot++;
    Next[tot]=Head[u];
    Head[u]=tot;
    E[tot]=Edge(v,w);
}

void spfa(int s)
{
    int l,r;
    memset(dis,0x3f,sizeof(dis));
    memset(vis,false,sizeof(vis));
    dis[s]=0; l=1; r=2; que[l]=s; vis[s]=true;
    while (l<r)
    {
        int u=que[l];
        l++; vis[u]=false;
        for (int i=Head[u];i;i=Next[i])
        {
            int v=E[i].v,w=E[i].w;
            if (dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if (!vis[v]) que[r++]=v,vis[v]=true;
            }
        }
    }
}

int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++)
    {
        int k=read();
        AddEdge(i,i-1,0),AddEdge(i-1,i,k);
    }
    for (int i=1;i<=m;i++)
    {
        int x,y,z;
        x=read(); y=read(); z=read();
        AddEdge(y,x-1,-z);      
    }
    spfa(n);
    cout<<-dis[0]<<endl;
    return 0;
}

T3 贪心

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll MAXN=5e5+100,MAXM=5e5+100;

struct node
{
    ll l,r,c;
}a[MAXM];
ll n,m,k[MAXN],tree[MAXN],BIT[MAXN],fa[MAXN];

ll read()
{
    ll w=0,ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') w=w*10+ch-48,ch=getchar();
    return w;   
}

bool cmp(const node &a,const node &b)
{
    if (a.l==b.l && a.r==b.r) return a.c>b.c;
    if (a.r==b.r) return a.l>b.l;
    return a.r<b.r;
}
//BIT;
void add(ll x,ll val)
{
    for (ll i=x;i<=n;i+=i&-i)
        BIT[i]+=val;
}
ll sum(ll x)
{
    ll ans=0;
    for (ll i=x;i>0;i-=i&-i)
        ans+=BIT[i];
    return ans;
}
ll sum(ll x,ll y)
{ return sum(y)-sum(x-1); }
//set;
void make_set()
{
    for (ll i=1;i<=n;i++) fa[i]=i;
}
ll getfa(ll x)
{
    if (fa[x]==x) return x;
    fa[x]=getfa(fa[x]);
    return fa[x];
}
void merge(ll x,ll y)
{
    x=getfa(x); y=getfa(y);
    if (x!=y)
        if (y>x) fa[y]=x;
        else fa[x]=y;
}
//solve;
void solve()
{
    make_set();
    for (ll i=1;i<=m;i++)
    {
        ll num=a[i].c;
        num-=sum(a[i].l,a[i].r);
        if (num<=0) continue;
        for (ll j=a[i].r;j>=a[i].l && num>0;j=getfa(j))
        {
        if (k[j]>tree[j])
            if (num<=k[j]-tree[j]) add(j,num),tree[j]+=num,num=0;
            else num-=k[j]-tree[j],add(j,k[j]-tree[j]),tree[j]=k[j];
        if (tree[j]==k[j]) merge(j-1,j);
        }
    }
}

int main()
{
    n=read(); m=read();
    for (ll i=1;i<=n;i++)
        k[i]=read();
    for (ll i=1;i<=m;i++)
        a[i].l=read(),a[i].r=read(),a[i].c=read();
    sort(a+1,a+1+m,cmp);
    solve();
    ll ans=0;
    for (ll i=1;i<=n;i++)
        ans+=tree[i];
    printf("%lld",ans); 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值