GYM 101147 F.Bishops Alliance(dp+BIT)

202 篇文章 1 订阅
54 篇文章 0 订阅

Description
一个棋盘,上面有一些棋子,第i个棋子有一个p[i]值,现在要选择在同一条对角线上的某些棋子,使得对任意两个棋子i和j,i到j之间所隔格子数不小于pi^2+pj^2+c,其中c是一给定常数,问最多可以选多少个棋子
Input
第一行一整数T表示用例组数,每组用例首先输入三个整数n,m,c分别表示棋盘规格,棋子数量和常数c,之后m行每行三个整数xi,yi,pi表示第i个棋子的位置和p值(1<=n<=1e5,1<=m<=1e5,0<=c<=1e5,1<=xi,yi,pi<=n)
Output
输出最多选多少棋子
Sample Input
1
8 5 2
7 7 1
4 4 1
3 5 1
2 6 2
4 8 1
Sample Output
2
Solution
考虑每一条对角线可以选多少棋子,取最大值即为答案,先考虑一条对角线上i,j,k三个棋子(i < j < k),如果i-j+1>=pi*pi+pj*pj+c,j-k+1>=pj*pj+pk*pk+c,那么i-k+1>=pi*pi+2*pj*pj+pk*pk+2*c-1>=pi*pi+pk*pk+c,说明该种关系具有传递性,那么就不需要两两去考虑所选棋子,只需要从小大到按顺序来即可,以dp[i]表示该条对角线上前i个中最多能选的棋子个数,那么dp[i]=max(dp[j])+1,其中j需满足i-j+1>=pi*pi+pj*pj+c,即-j+pj*pj>=pi*pi-i+c-1,把所有的-i-pi*pi和i+pi*pi+c-1存起来离散化一下,用BIT维护前缀dp[i]的最大值,离散化的值代表在BIT中要插入的位置,处理到一个i时,查询i+pi*pi+c-1离散化后的值在BIT中对应的位置的前缀最大值即为max(dp[j]),加一就是dp[i]的值,再在-i-pi*pi离散化后的值对应的位置处插入dp[i]的值即可,实时维护dp[i]的最大值ans即为最终答案
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 111111
typedef pair<int,ll>P;
vector<P>v[2][2*maxn];
int T,n,m,c;
ll h[2*maxn];
struct BIT
{
    #define lowbit(x) (x&(-x))
    ll b[2*maxn];
    int m;
    void init(int n)
    {
        m=n;
        for(int i=1;i<=n;i++)b[i]=0;
    }
    void update(int x,ll v)
    {
        while(x<=m)b[x]=max(b[x],v),x+=lowbit(x);
    }
    ll query(int x)
    {
        ll ans=b[x];
        while(x)ans=max(ans,b[x]),x-=lowbit(x);
        return ans;
    }
}bit;
int main()
{
    freopen("bishops.in","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&c);
        for(int i=0;i<2;i++)
            for(int j=1;j<2*n;j++)
                v[i][j].clear();
        while(m--)
        {
            int x,y,p;
            scanf("%d%d%d",&x,&y,&p);
            v[0][x+y-1].push_back(P(x,1ll*p*p));
            v[1][x-y+n].push_back(P(x,1ll*p*p));
        }
        int ans=0;
        for(int i=0;i<2;i++)
            for(int j=1;j<2*n;j++)
            {
                sort(v[i][j].begin(),v[i][j].end());
                int res=0;
                for(int k=0;k<v[i][j].size();k++)
                {
                    int x=v[i][j][k].first;
                    ll p=v[i][j][k].second;
                    h[res++]=-p-x,h[res++]=p-x+c-1;
                }
                sort(h,h+res);
                res=unique(h,h+res)-h;
                bit.init(res);
                for(int k=0;k<v[i][j].size();k++)
                {
                    int x=v[i][j][k].first;
                    ll p=v[i][j][k].second;
                    int pos=lower_bound(h,h+res,p-x+c-1)-h;
                    pos=res-pos;
                    int num=bit.query(pos);
                    ans=max(ans,num+1);
                    pos=lower_bound(h,h+res,-p-x)-h;
                    pos=res-pos;
                    bit.update(pos,num+1);
                }
            }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值