杭电 HDU 5596 (详解)GTW likes gt(思维)

GTW likes gt

 
 Accepts: 54
 
 Submissions: 782
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
从前,有nn只萌萌的GT,他们分成了两组在一起玩游戏。他们会排列成一排,第ii只GT会随机得到一个能力值b_ibi。在第ii秒的时候,第ii只GT可以消灭掉所有排在他前面的和他不是同一组的且能力值小于他的GT。
为了使游戏更加有趣,GT的首领GTW会发功mm次,第ii次发功的时间为c_ici,则在第c_ici秒结束后,b_1,b_2,...,b_{c_i}b1,b2,...,bci都会增加1。
现在,GTW想知道在第nn秒之后,会有几只GT存活下来。
输入描述
第一行只有一个整数T(T\leq 5)T(T5),表示测试数据组数。
第二行有两个整数n,mn,m。表示GT的个数和GTW发功的次数。(1\leq n \leq 50000,1\leq m\leq 500001n50000,1m50000)
第三到n+2n+2行,每行有两个整数a_i,b_iai,bi,表示第ii只GT在哪个组和他的能力值 (0\leq a[i]\leq 1,1\leq b[i]\leq 10^6)(0a[i]1,1b[i]106)n+3n+3行到第n+m+2n+m+2行,每行有一个整数c_ici,表示GTW第ii次发功的时间。1\leq c[i]\leq n1c[i]n
输出描述
总共TT行,第ii行表示第ii组数据中,GT存活的个数。
输入样例
1
4 3
0 3
1 2
0 3
1 1
1
3
4
输出样例
3
Hint
11秒后 能力值为4\ 2\ 3\ 14 2 3 122秒后 能力值为4\ 2\ 3\ 14 2 3 133秒后 能力值为5\ 3\ 4\ 15 3 4 1,第22只GT被第33只GT消灭掉了
第44秒后 能力值为6\ 4\ 5\ 26 4 5 2
c_ici并不是有序的


题意很明确,这里就不多啰嗦了。

这里详说一下我的解题经历和大牛代码的解题思想。

首先看到题意 ,小白嘛,当然是想用暴力或者模拟思路去搞定这

int a[50050];
int b[50050];
int fagong[50050];

        memset(fagong,0,sizeof(fagong));
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
        }
        for(int i=0;i<m;i++)
        {
            int k;
            scanf("%d",&k);
            fagong[k]++;//题意是说可以发功多次~。所以这里每出现一次,我们就要+1.
        }

个题目,但是读题目中的数据范围的时候,就知道这个方法根本不可行。然后就想到了用线段树,多次操作,单次查询。这里用线段树可解。然后我就敲啊敲啊,敲出了我的线段树,但是不巧TLE,我估计是后台数据比较强大的一放面,并且自己线段树的代码中数据的操作可能有错误,再一个就是可能是代码这么做确实可以超时,不管如何,反正没过,这里希望如果有大牛看出来我这个为什么没有AC的话还请指点。最后上我的超时代码.

还是研究了好一会,并没有摆脱TLE的命运。然后默默的去找了题解,发现这个题目的解题思路是真的很巧。

这里我们用样例来举例详解。这里我们先上初始化内容代码:


这里我们知道,最后一个GT是不会死的。另外,发功是随着比较进行的,我们这里的比较步骤是这样的:先发功,然后由当前的GT向前比较的,看看前边有没有比自己小的GT.然后干掉他。如果按照模拟去做,我们这里发功的操作的时间复杂度是O(n^2)。是一定会超时的,所以我们这里形成了这样的一个巧发功的方法:逆向思维。很多时候呢,我们正向解决问题比较困难的时候,要多逆向考虑考虑,也许发现真的可行呢~。

我们这里还是按照样例来说:

初始的b是这样的:3 2 3 1

整个操作过程完成后B是这样的:6 4 5 2.

我们不难发现,当时间变到4的时候发功之后,1-4号GT的b都要+1,但是时间变到3的时候,4号GT是不会跟着变化的,由于这个特性,我们可以逆向的去+b,就达到了降低时间复杂度的任务:

        for(int i=n;i>=1;i--)
        {
            fagong[i]+=fagong[i+1];
            b[i]+=fagong[i];
        }
b处理完毕之后,我们对其中数据进行分析:6 4 5 2。

首先呢,我们要知道,是要对不同阵容的比较。所以我们这里先设上两个变量:maxn,maxn1.分别表示0号阵容的最大b,和1号阵容的最大b

如果我们这里还是正向比较。复杂度还是O(n^2)所以,这里还是用上了逆向思维。如果当前b的后边的最大不同阵容的b不足以杀死当前b,那么他就生存下来了。(这一步骤很容易理解,并且也是最重要的思路。)(大牛就是大牛。。。。。小白给跪了.....)

最终我们这里就形成了近乎完美的核心思路:这里直接上代码:

        int output=0;
        int maxn0=-0x1f1f1f1f;
        int maxn1=-0x1f1f1f1f;
        for(int i=n;i>=1;i--)
        {
            if(a[i]==0)//如果是0阵容的GT
            {
                if(maxn1<=b[i])//如果当前这只GT的身后没有任何一只GT(当然是1阵容的)能够干掉他,
                output++;//那么他就生存了下来
                maxn0=max(maxn0,b[i]);//实时更新身后最大值。
            }
            if(a[i]==1)
            {
                if(maxn0<=b[i])
                output++;
                maxn1=max(maxn1,b[i]);
            }
        }
        printf("%d\n",output);
然后是完整的AC代码和我的超时线段树代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int a[50050];
int b[50050];
int fagong[50050];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(fagong,0,sizeof(fagong));
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
        }
        for(int i=0;i<m;i++)
        {
            int k;
            scanf("%d",&k);
            fagong[k]++;
        }
        for(int i=n;i>=1;i--)//NB//代替了sort并且双for.
        {
            fagong[i]+=fagong[i+1];
            b[i]+=fagong[i];
        }

        int output=0;
        int maxn0=-0x1f1f1f1f;
        int maxn1=-0x1f1f1f1f;
        for(int i=n;i>=1;i--)
        {
            if(a[i]==0)
            {
                if(maxn1<=b[i])
                output++;
                maxn0=max(maxn0,b[i]);
            }
            if(a[i]==1)
            {
                if(maxn0<=b[i])
                output++;
                maxn1=max(maxn1,b[i]);
            }
        }
        printf("%d\n",output);
    }
}
如果某位大牛看出了我的代码哪里还能优化或者是错误了的话,请您务必指出:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define lson l,m,rt*2
#define rson m+1,r,rt*2+1
int shuju[121212];
int tree[121212];
int flag[121212];
int bijiao[121212];
int bijiao2[121212];
int num[121212];
int fagong[121212];
int cont;
void pushup(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void build( int l ,int r , int rt )
{
    if( l == r )
    {
        scanf("%d%d",&flag[rt],&shuju[rt]);
        num[rt]=cont;
        bijiao[cont]=shuju[rt];
        bijiao2[cont]=flag[rt];
        cont++;
        tree[rt]=1;
        return ;
    }
    else
    {
        int m = (l+r)>>1 ;
        build(lson) ;
        build(rson) ;
        pushup(rt) ;
    }
}
void update(int c,int l,int r,int rt)
{
    if(l==r)
    {
        shuju[rt]+=c;
        bijiao[num[rt]]+=c;
        return ;
    }
    else
    {
        int m=(l+r)>>1 ;
        update(c,lson);
        update(c,rson);
    }
}
void update2(int c,int c2,int p,int l,int r,int rt)
{
    if(l==r)
    {
        if(c>shuju[rt]&&p>num[rt]&&c2!=flag[rt])
        tree[rt]=0;
        return ;
    }
    else
    {
        int m=(l+r)>>1 ;
        update2(c,c2,p,lson);
        update2(c,c2,p,rson);
        pushup(rt);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(fagong,0,sizeof(fagong));
        cont=0;
        int n,m;
        scanf("%d%d",&n,&m);
        build(1,n,1);
        //printf("yes\n");
        int fagong[10000];
        for(int i=0;i<m;i++)
        {
            int k;
            scanf("%d",&k);
            k--;
            fagong[k]++;
        }
        sort(fagong,fagong+m);
        for(int i=0;i<n;i++)
        {
            //printf("%d\n",bijiao[i]);
            if(fagong[i])update(fagong[i],1,n,1);
            update2(bijiao[i],bijiao2[i],i,1,n,1);
        }
        printf("%d\n",tree[1]);
    }
}




















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值