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

时间:2022-12-15 23:19:32

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 1
22秒后 能力值为4\ 2\ 3\ 14 2 3 1
33秒后 能力值为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]);
}
}