刷题#R7

100 篇文章 0 订阅
60 篇文章 0 订阅

这里写图片描述


【问题描述】
给定一个可重集合,一开始只有一个元素 0 。然后你可以操作若干轮,每一
轮,你需要对于集合中的每个元素 x 进行如下三种操作之一:
1 、将 x 变为 1 + x 。
2 、将 x 分裂为两个非负整数 z y, ,且满足 z y x + = 。
3 、什么都不做。
每一轮,集合中的每个元素都必须进行上面三个操作之一。
对于一个最终的集合,你的任务是判断至少进行了多少轮。
【输入文件】
第一行为一个正整数 n ,表示集合的最终大小。
第二行为 n 个非负整数,描述集合中的元素。
【输出文件】
输出一个非负整数,为最少的轮数。
【输入输出样例】
multiset.in multiset.out
5
0 0 0 3 3
5
【数据规模和约定】
设集合中最大的元素为 m 。
对于 % 10 的数据,满足 10 ≤ n , 10 ≤ m 。
对于 % 30 的数据,满足 50 ≤ n , 100 ≤ m 。
对于 % 50 的数据,满足 1000 ≤ n , 10000 ≤ m 。
对于 % 100 的数据,满足 1000000 1 ≤ ≤ n , 1000000 0 ≤ ≤ m 。

【问题描述】
!国是一个由”个城市构成的国家。这”个城市从1到”进行编号。其中,城
市1是!国中资源产出最多的城市,而城市”是!国唯一的港口的所在地。由于这
两个城市之间距离很远,所以!国没有直接从城市1向城市”修建道路。不过,
很多城市之间修建了一些单向通行的道路。从城市1经过若干条道路,是可以到
达城市”的。
城市中的单向道路总共有m条,从1到m进行编号。为了方便管理,国家的
统治者希望能把这些道路分成若干组,每组道路的编号都是连续的。同时,出
于一些奇怪的原因,一种合法的分组方式还需要满足:不存在一条从城市1到城
市”的路径,使得路径上的每条道路都属于同一组。
你需要计算:在上述条件的限制下,!国中的道路至少要被分成多少组?
【输入文件】
输入文件为 road. in。
输入文件第一行为两个正整数n,m,分别表示城市数及道路数。
接下来m行,每行两个正整数x,y,表示一条x到y的单向道路。
【输出文件】
输入文件为 road.out。
输出一个正整数,为最少的道路组数.
【输入输出样例】
road.in road.out
3 4
1 2
2 3
1 2
2 3
4
【数据规模和约定】
对于30%的数据,满足N≤ 100,M≤ 100。
对于50%的数据,满足N≤ 2000,M≤ 10000。
对于100%的数据,满足1 ≤N≤ 200000,1 ≤M≤ 500000。

【问题描述】
对于一名DotA玩家,补兵的个数是衡量一名选手的能力的一
项重要指标。特别是在打路人局的时候,补兵的能力就更加关键,因为常常会
有队友和你抢补刀。比如,队友操控的老鹿开着大在收一波兵,你操作的英雄
如果是幽鬼,就需要在老鹿的AOE中偷偷补上几刀来保证自己的发育。
我们可以建立如下模型来大致模拟这种状况:现在有”个小兵,每个小兵都
有自己的血量(血量一定是正整数)。你和老鹿轮流对小兵进行攻击。每次,
你可以选择对某个小兵造成1点伤害(或者你可以选择不造成任何伤害);接
着,老鹿会对所有小兵都造成1点伤害。如此往复,直到所有小兵都死亡(血量
达到0)。在这一过程中,如果你对某个小兵造成了致命伤害(使它的血量由1
变为0),那么你就算成功补到这个兵。
对于给定的情形,你需要计算你的最大补兵数。
【输入文件】
输入文件为 cs.in。
本题含有多组数据,第一行为数据组数T。
对于每组数据,第一行为一个正整数”,第二行为”个正整数,表示每个小
兵的初始血量。
【输出文件】
输入文件为 cs.out。
对于每组数据,输出一行一个整数,表示你的最大补兵数。
【输入输出样例】
cs.in cs.out
1
5
5 5 5 5 5
2
【数据规模和约定】
对于30%的数据,满足N ≤ 100。
对于50%的数据,满足N≤ 500,T≤ 30,每个小兵的血量不超过500。
对于100%的数据,满足1 ≤ N ≤ 1000,1 ≤ T ≤ 70。每个小兵的血量不超
过1000。数据规模有一定的梯度。

T1
我们可以把这个过程倒着模拟。
那么就要用尽量少的操作轮数来得到一个0的序列。
我们可以先把所有的数降序排一下序,然后把0尽量合并,把非零的数都减一。
这里需要说明一下,把非零数都减为零后在合并一定比先把非零数合并后再减更优。
因为非零数越多,每次可减下去的和就越多。
还有一点,把一个非零数和0合并后再减为0,和先把非零数减为0再合并是一样的。
举个例子:
3 3 3 1 1 0 0
演变:
2 2 2 0 0 0
1 1 1 0 0
0 0 0 0
0 0
0
共五次
如此模拟即可。
至于把前面的都减一,我们可以找一个标准,每一轮把这个标准加一。

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=1000009;
int n,m,a[N],ans,num;
bool cmp(int x,int y)
{
    return x>y;
}
int main()
{
    freopen("multiset.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1,cmp);
    int l=n,r=n;
    while(a[l]==0) l--;
    int t=1;
    //l=min(l,r);
    while(r>1)
    {
        r-=(r-l)/2;
        while(a[l]==t) l--;
        ans++;t++;
    }
    printf("%d",ans);
    return 0;
}

T2
60分可暴力得到。
每次先把新的边加进当前集合,然后bfs一遍,如果1能够到达n,那就把当前集合清空,然后把这条边当做新集合的第一条边。(真的好暴力)
在测试时先打了个并查集,后来找出反例证明出不正确,又重新打的暴力;
评测时,竟然发现,错误的并查集比正确的暴力多得了10分qaq.
100分:在60分的基础上加的优化;
我们可以用倍增的方法来找当前这一段(这个集合)最多能加到哪条边,然后再如此求下一个集合。
还是T了两个点;
看别人有跑得很快的而且也不是倍增的方法,但是看不太懂。

60分

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
const int N=200009;
const int M=500009;
int n,m,f[N],ans;
int L,R,mid;
struct H{
    int s,t;
}q[M];
int head[N],nxt[M],to[M],tot;
bool vis[N];
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
int bfs(int s)
{
    queue <int> qu;
    memset(vis,0,sizeof(vis));
    qu.push(s);
    vis[s]=1;
    while(!qu.empty())
    {
        int x=qu.front();
        qu.pop();
        if(x==n) return 1;
        for(int i=head[x];i;i=nxt[i])
        {
            if(!vis[to[i]])
            {
                qu.push(to[i]);
                vis[to[i]]=1;
            }
            if(to[i]==n) return 1;
        }
    }
    return 0;
}
int get()
{
    int cnt=1;
    for(int i=1;i<=m;i++)
    {
        add(q[i].s,q[i].t);
        int can=bfs(1);
        if(can) 
        {
            tot=0;
            memset(head,0,sizeof(head));
            memset(to,0,sizeof(to));
            memset(nxt,0,sizeof(nxt));  
            add(q[i].s,q[i].t);
            cnt++;      
        }
    }
    return cnt;
}
int main()
{
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&q[i].s,&q[i].t);
    ans=get();
    printf("%d\n",ans);
    return 0;
}

80分

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
const int N=200009;
const int M=500009;
int n,m,ans;
struct H{
    int s,t;
}q[M];
int head[N],nxt[M],to[M],tot,tt;
bool vis[N];
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
bool bfs(int s)//1能否到达n 
{
    queue <int> qu;
    memset(vis,0,sizeof(vis));
    qu.push(s);
    vis[s]=1;
    while(!qu.empty())
    {
        int x=qu.front();
        qu.pop();
        if(x==n) return 1;
        for(int i=head[x];i;i=nxt[i])
        if(i>tt&&i<=tot)
        {
            if(!vis[to[i]])
            {
                qu.push(to[i]);
                vis[to[i]]=1;
            }
            if(to[i]==n) return 1;
        }
    }
    return 0;
}
bool check(int start,int last)
{
    //tot=0;
    //memset(head,0,sizeof(head));
    //memset(to,0,sizeof(to));
    //memset(nxt,0,sizeof(nxt));
    for(int i=1;i<=n;i++) head[i]=0;
    for(int i=1;i<=tot;i++) nxt[i]=to[i]=0;
    tot=0;

    //tt=tot;

    for(int i=start;i<=last;i++)
     add(q[i].s,q[i].t);
    return bfs(1);
}
int get()
{
    int now=1,cnt=0;
    while(now<=m)
    {
        int i;
        for(i=1;now+i<=m;i<<=1)//倍增找最远的 
           if(check(now,now+i)) break;
        i>>=1;
        int nowx=now+i;
        for(;i>0;i>>=1)
        {
            if(nowx+i<=m&&(!check(now,nowx+i)))
              nowx+=i;
        } 
        cnt++;
        now=nowx+1;
    }
    return cnt;
}
int main()
{
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&q[i].s,&q[i].t);
    ans=get();
    printf("%d\n",ans);
    return 0;
}

根本就是错的并查集(70分)
后面附有反例

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=200009;
const int M=500009;
int n,m,f[N],ans;
int L,R,mid;
struct H{
    int s,t;
}q[M];
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
int get()
{
    for(int i=1;i<=n;i++) f[i]=i;
    int cnt=1;
    for(int i=1;i<=m;i++)
    {
        int fs=find(q[i].s);
        int ft=find(q[i].t);
        int ff=find(1);
        int fn=find(n);
        if(fs==ff&&ft==fn)
        {
            cnt++;
            for(int j=1;j<=n;j++) f[j]=j;
            f[q[i].t]=q[i].s;
        }
        else f[ft]=fs;
    }
    return cnt;
}
int main()
{
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&q[i].s,&q[i].t);
    ans=get();
    printf("%d\n",ans);
    return 0;
}
/*
5
4
1 5
3 5
3 6
4 3

1
*/
//反例

T3
正解:dp
测试提交的时候没做到这个题呢qwq.

总结:
T1 搞了近一个小时,终于开始写,别人都写好长时间了;还好最后搞出了正解;noip的时候这样好害怕。
T2 又搞了一个半小时,发现想的不对,匆匆花10分钟打了暴力,辛亏暴力比较好写。
很让我哭笑不得的是,一开始认为想了一个半小时是错的并查集,竟然比暴力多10分。
T3 由于前两个题花去了所有的时间,连暴力也没有打啊。
期望得分:100+60+0
实际得分:100+60+0
还算可以,但是策略还是有风险,继续加油。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值