GTW likes gt
Accepts: 54
Submissions: 782
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
从前,有n只萌萌的GT,他们分成了两组在一起玩游戏。他们会排列成一排,第i只GT会随机得到一个能力值bi。在第i秒的时候,第i只GT可以消灭掉所有排在他前面的和他不是同一组的且能力值小于他的GT。 为了使游戏更加有趣,GT的首领GTW会发功m次,第i次发功的时间为ci,则在第ci秒结束后,b1,b2,...,bci都会增加1。 现在,GTW想知道在第n秒之后,会有几只GT存活下来。
输入描述
第一行只有一个整数T(T≤5),表示测试数据组数。 第二行有两个整数n,m。表示GT的个数和GTW发功的次数。(1≤n≤50000,1≤m≤50000) 第三到n+2行,每行有两个整数ai,bi,表示第i只GT在哪个组和他的能力值 (0≤a[i]≤1,1≤b[i]≤106) 第n+3行到第n+m+2行,每行有一个整数ci,表示GTW第i次发功的时间。1≤c[i]≤n
输出描述
总共T行,第i行表示第i组数据中,GT存活的个数。
输入样例
1 4 3 0 3 1 2 0 3 1 1 1 3 4
输出样例
3
Hint
第1秒后 能力值为4 2 3 1 第2秒后 能力值为4 2 3 1 第3秒后 能力值为5 3 4 1,第2只GT被第3只GT消灭掉了 第4秒后 能力值为6 4 5 2 ci并不是有序的
思路:
这题我一开始更不毫无头绪,最后也没做出来。最困惑的是如何在每一秒中更新【1,c【i】】的值。结果看了题解后发现自己的思路是认为要从前向后去做,而这里就隐藏了一个解题关键,每次更新在前面的数都是会不断被更新的,而这个更新是不会影响后面的一致性。
例如:
第3秒后 能力值为5 3 4 1 第4秒后 能力值为6 4 5 2这里的5-4,与下面的6-5的关系是不改变的。所以我们就知道从后到前是可行的。
有了上面的基础后就是贪心了,所以我只要用两个变量去代表两个组的最大值,那么我只要比较后面的数是否比这两个
变量小,如果小则说明是可以将他杀掉。
线段树AC代码:
#include<iostream> #include<algorithm> #include<cstring> #include<string> #include<cstdio> using namespace std; #define T 100005 #define inf 0x3f3f3f3f3f3f3f3fL #define lson (rt<<1) #define rson (rt<<1|1) struct node { int L,R,mid; int sum,lazy; }tree[T<<2]; struct no { int num,v; }a[T]; void Pushdown(int rt) { int u = tree[rt].lazy/*,v = (tree[rt].R-tree[rt].L+1)*/; if(u){ /* tree[rson].sum += v*u; tree[lson].sum += v*u;*/ tree[rson].lazy += u; tree[lson].lazy += u; tree[rt].lazy = 0; } } void build(int rt,int L,int R) { tree[rt].L = L,tree[rt].R = R; tree[rt].mid = (L+R)>>1; tree[rt].lazy = 0; if(L==R){ tree[rt].sum=a[L].v; return; } build(lson,L,tree[rt].mid); build(rson,tree[rt].mid+1,R); } void update(int rt,int L,int R) { if(L<=tree[rt].L&&tree[rt].R<=R){ tree[rt].lazy ++; return; } Pushdown(rt); if(R<=tree[rt].mid){ update(lson,L,R); } else if(L>tree[rt].mid) { update(rson,L,R); } else { update(lson,L,tree[rt].mid); update(rson,tree[rt].mid+1,R); } } void query(int rt) { if(tree[rt].L==tree[rt].R){ a[tree[rt].L].v = tree[rt].lazy+tree[rt].sum; return; } Pushdown(rt); query(lson); query(rson); } int sum[T]; int main() { #ifdef zsc freopen("input.txt","r",stdin); #endif int N,n,m,i,j,k; scanf("%d",&N); while(N--) { scanf("%d%d",&n,&m); for(i=1;i<=n;++i){ scanf("%d%d",&a[i].num,&a[i].v); } build(1,1,n); for(i=0;i<m;++i){ scanf("%d",&k); update(1,1,k); } query(1); /*for(i=1;i<=n;++i){ printf("%d ",a[i].v); } printf("\n");*/ int ma[2]={0},c=0; for(i=n;i>0;--i){ if(a[i].v>ma[a[i].num]){ ma[a[i].num] = max(a[i].v,ma[a[i].num]); } if(a[i].v<ma[!a[i].num]){ c++; } } printf("%d\n",n-c); } return 0; }
AC代码:
#include<iostream> #define cal(x,y) memset(x,y,sizeof(x)) #define T 50010 using namespace std; int N,n,m,i,j,k; int flag[T],num[T],w[T],v[T]; int main() { #ifdef zsc freopen("input.txt","r",stdin); #endif scanf("%d",&N); while(N--) { scanf("%d%d",&n,&m); cal(v,0);cal(flag,0); for(i=1;i<=n;++i){ scanf("%d%d",num+i,w+i); } for(i=1;i<=m;++i){ scanf("%d",&j); flag[j]++;//标记要加一的数 } int vis[2] ={0};//这个数组非常巧妙,实现了两个组中的双互切换 int cnt = 0; for(i=n;i>0;--i){ cnt += flag[i];//后面的数要加的值 int tmp = w[i] + cnt;//n秒后的真是数值 if(tmp<vis[!num[i]]){//少于前面的数,减去 v[i]=1; } if(vis[num[i]]<tmp){//更新两个组的最大值 vis[num[i]]=tmp; } } int c =n; for(i=1;i<=n;++i){ if(v[i])c--; } printf("%d\n",c); } return 0; }