Bzoj 4548: 小奇的糖果(双向链表+排序+树状数组)

以下内容来自ShallWe's Blog

题目

4548: 小奇的糖果

Description

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

Input

包含多组测试数据,第一行输入一个正整数\(T\)表示测试数据组数。
接下来\(T\)组测试数据,对于每组测试数据,第一行输入两个正整数\(N\),\(K\),分别表示点数和颜色数。
接下来\(N\)行,每行描述一个点,前两个数\(x\),\(y\)(\(|x|,|y|≤2^30-1\))描述点的位置,最后一个数\(z\)\((1≤z≤k)\)描述点的颜色。
对于\(100%\)的数据,\(N≤100000,K≤100000,T≤3\)

Output

对于每组数据在一行内输出一个非负整数\(ans\),表示答案

解题报告

考场上光彩爆零->写了一个很长很长的臭程序,先讲一下考场上的思路:
很容易看出,可以把合法的线段造成的收益看做矩形内部节点数,矩形内部节点数很好求,所以就是要找出矩形.
矩形显然有三类,一类是枚举\(x\)相邻的相同颜色\(star\),之间通天遁地的矩形,一类是下边贴一个\(star\),两边各贴一个相同颜色的\(star\),上边贴顶的矩形,第三类和第二类相似;
所以我就将问题转化成求一个点两端的比它高(低)的第一对点的横坐标;
我现场是用单调栈来搞的,每一个颜色维护上方一个单调递减的栈,每次弹栈就形成矩形,这样每个点可以形成上下两个矩形,加上第一类矩形,一共是\(3n\)个矩形;


上面那个方法难写难调细节很多;学习了hzwer的姿势,使用双向链表实际上作用就是维护两边最近的相同颜色的点,考虑如果当前的点是该颜色点中最高的那么双向链表所指的横坐标就能确定矩形左右边界,当这个矩形确定后,这个最高点会对两侧点造成干扰,删掉就好。这样下矩形就确定好了,上矩形将纵坐标上下翻转就好了,
提一句:求矩形内部节点个数->扫描线+树状数组/树套树随便做。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=100001;
struct poi{
    int x,y,color,id; 
    poi(){}
    poi(int x,int y,int color,int id)
        :x(x),y(y),color(color),id(id){}
}   point[N];
int T,n,k,ans;
int l[N],r[N],bit[N],vec[N],pre[N],w[N];
inline void in(int &x){
    char ch=getchar(); 
    int f=1; 
    for (;ch<'0'||ch>'9';ch=getchar())
        if (ch=='-') f=-1; 
    for (x=0;ch>='0'&&ch<='9';ch=getchar())
        x=x*10+ch-48; 
    x=x*f; 
}
inline void init(){
    ans=0; 
}
bool cmpx(poi a,poi b){
    return a.x<b.x;
}
bool cmpy(poi a,poi b){
    return a.y<b.y;
}
inline void add(int x,int val){
    for (;x<=n+1;x+=x&-x)
        bit[x]+=val; 
}
inline int query(int x,int tmp=0){
    for (;x;x-=x&-x)
        tmp+=bit[x]; 
    return tmp;
}
inline void up(int l,int r){
    if (l>r) return;
    int tmp=query(r)-query(l-1); 
    ans=max(tmp,ans); 
}   
void solve(){
    memset(bit,0,sizeof(bit));
    memset(pre,0,sizeof(pre)); 
    w[0]=0,w[n+1]=n+1;
    sort(point+1,point+1+n,cmpx); 
    for (int i=1;i<=n;i++) 
        add(point[i].x,1); 
    int y,x;
    for (int i=1;i<=n;i++){
        x=point[i].id,y=pre[point[i].color];
        l[x]=y,r[x]=n+1; 
        if (y) r[y]=x; 
        up(w[y]+1,w[x]-1); 
        pre[point[i].color]=x;
    }
    for (int i=1;i<=k;i++){
        up(w[pre[i]]+1,n+1); 
    }
    sort(point+1,point+1+n,cmpy); 
    for (int i=1,j=1;i<=n;i++){
        x=point[i].id; 
        while (j<=n&&point[j].y==point[i].y){
            add(point[j].x,-1);
            j++;
        }
        l[r[x]]=l[x],r[l[x]]=r[x];
        up(w[l[x]]+1,w[r[x]]-1); 
    }
}
int main(){
//  freopen("candy.in","r",stdin); 
//  freopen("candy.out","w",stdout); 
    in(T);
    int x,y,z;
    while (T--){
        init();
        in(n),in(k); 
        for (int i=1;i<=n;i++){
            in(x),in(y),in(z); 
            point[i]=poi(x,y,z,i);
        }
        for (int i=1;i<=n;i++) 
            vec[i]=point[i].x; 
        sort(vec+1,vec+1+n); 
        for (int i=1;i<=n;i++){ 
            point[i].x=lower_bound(vec+1,vec+1+n,point[i].x)-vec;
            w[i]=point[i].x;
        } 
        solve(); 
        for (int i=1;i<=n;i++)
            point[i].y=-point[i].y; 
        solve(); 
        printf("%d\n",ans); 
    }
    return 0; 
}

双倍经验:3658: Jabberwocky

转载于:https://www.cnblogs.com/ShallWe2000/p/5776598.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值