【bzoj 4548】【JZOJ 5229】 小奇的糖果

13 篇文章 0 订阅
4 篇文章 0 订阅

Description

有 N 个彩色糖果在平面上。小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果。求出最多能够拾起多少糖果,使得获得的糖果包含所有的颜色。

对于 30% 的数据,N ≤ 100;
对于 60% 的数据,N ≤ 5000;
对于 100% 的数据,N ≤ 100000,K ≤ 100000,T ≤ 3。

Preface

比赛的时候一直以为是分治+数据结构套路题,不敢打
正解却是乱搞,说明不可陷入惯性思维,看起来类似的题,解法也可能大相径庭

30%

枚举线段两个端点,再O(n)按从上到下或从下到上的顺序扫描每一个点判断
O(n^3)

60%

正解写炸的
不知道有什么O(n^2logn)的做法,也不知道能不能过这一档

100%

考虑一个合法且尽量优的矩阵满足什么条件
其左右边界肯定顶着一对相同的颜色(被该颜色卡住而不能继续扩展)
方便叙述,此处只讨论在选择线段上方的情况
假若维护一条水平线,表示线段高度
水平线从下往上扫,线段的两个端点显然会不断往两边扩展
如果扫的时候遇到一个点,其颜色为x,那么它的贡献是什么?
那么这个点被删除,之后该颜色的限制放宽
其它线段的端点原本被该点卡住,现在可以扩展了,需要更新,可以用双向链表维护
同时为了查询区域点数,使用树状数组维护

从上往下扫类似
O(nlogn)

Think Further

考虑类似问题:同样是线段上方或下方,求最少包含多少个点,使得包含所有颜色?
大致idea:维护水平线扫描,同时不断将线段两端收紧

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
const int N=1e5+5;
void read(int &t)
{
    t=0;
    int p=1;char ch;
    for(ch=getchar();ch<'0' || ch>'9';ch=getchar())
        if(ch=='-') p=-1;
    for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';
    t*=p;
}
int n,m,num,ans,x[N],jx[N],jy[N],c[N],tr[N],last[N],L[N],R[N];
struct node
{
    int x,y,z,id;
}a[N],b[N];
bool cmpx(node a,node b){return a.x<b.x;}
bool cmpy(node a,node b){return a.y<b.y;}
int lowbit(int x){return x&-x;}
void add(int x,int z)
{
    for(;x<=n;x+=lowbit(x)) tr[x]+=z;
}
int query(int x)
{
    int t=0;
    for(;x;x-=lowbit(x)) t+=tr[x];
    return t;
}
int get(int l,int r)
{
    return query(r)-query(l-1);
}
void solve()
{
    memset(last,0,sizeof(last));
    sort(a+1,a+n+1,cmpx);
    fo(i,1,n) add(a[i].x,1);
    fo(i,1,n)
    {
        int id=a[i].id,lst=last[a[i].z];
        L[id]=lst,R[id]=n+1;
        if(lst) R[lst]=id;
        ans=max(ans,get(x[lst]+1,x[id]-1));
        last[a[i].z]=id;
    }
    fo(i,1,m) ans=max(ans,get(x[last[i]]+1,n));
    sort(a+1,a+n+1,cmpy);
    int lst=1;
    for(int i=1,j=1;i<=n;i++)
    {
        int id=a[i].id;
        for(;j<=n && a[j].y==a[i].y;j++) add(a[j].x,-1);
        L[R[id]]=L[id],R[L[id]]=R[id];
        ans=max(ans,get(x[L[id]]+1,x[R[id]]-1));
    }
}
int main()
{
    int _;
    for(read(_);_;_--)
    {
        memset(a,0,sizeof(a));
        ans=0;
        read(n);read(m);
        fo(i,1,n) read(a[i].x),read(a[i].y),read(a[i].z),a[i].id=i,jx[i]=a[i].x;
        sort(jx+1,jx+n+1);
        fo(i,1,n) x[i]=a[i].x=lower_bound(jx+1,jx+n+1,a[i].x)-jx;
        x[n+1]=n+1;
        solve();
        fo(i,1,n) a[i].y=-a[i].y;
        solve();
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值