gym101147 Bishops Alliance

题目大意:在一个n*n的棋盘上有一些棋子,每个棋子i有一个值pi。有一个常数c。有一棋子集合定义如下,集合中所有棋子都在同一对角线上,因为在同一对角线上,其横坐标可以标定棋子的位置,集合中任意两个棋子i,j(i<j)之间满足j-i+1>=pj^2+pi^2+c。求最大的集合。

题解:

首先这个集合中的元素具有传递性,若i,j(i<j)属于同一个集合,且j,k(j<k)属于同一个集合,则i,k属于同一集合。

可以用dp求最大集合,dp[i]表示第i个元素结尾能够取得的最大集合大小。然后在前面找到满足i-j+1>=pi^2+pj^2+c的dp[j]值最大的j。注意到移项之后上式变为-pj^2-j>=pi^2-i+c-1,这样就可以进行离散化,用树状数组维护最大值。

解法2:

能够通过集合的定义发现,可以将每个棋子看作是一条线段,最后要求的其实就是最大不相交线段覆盖。这是一个经典问题,可贪心解。



#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn = 100010;

struct node{
    int x;shuzhuang
    ll p;
    node(int x,ll p):x(x),p(p){}
    node(){}
    bool operator<(const node &a)const{
        return x<a.x;
    }
};

vector<node> ar[2][maxn<<1];
ll des[maxn<<1];
int nn;
int bit[maxn<<1];

int lowbit(int x)
{
    return x&(-x);
}


void init(int n)
{
    for(int i = 1; i <= n; i++) bit[i] = 0;
    for(int i = 1; i <= n; i++)
    {
        int l = i + lowbit(i);
        bit[l] = max(bit[l],bit[i]);
    }
}

int query(int n)
{
    int ans = 0;
    while(n)
    {
        ans =max(ans,bit[n]);
        n -= lowbit(n);
    }
    return ans;
}

void update(int pos,int change)
{
    while(pos <= maxn)
    {
        bit[pos] = max(bit[pos],change);
        pos += lowbit(pos);
    }
}

bool cmp(int a,int b){
    return a>b;
}

int _find(ll x){
    int ret;
    ret = lower_bound(des,des+nn,x)-des;
    ret = nn-ret;
    //printf("x = %I64d id = %d\n",x,ret);
    return ret;
}


int main(){
    freopen("bishops.in","r",stdin);
    int T,n,m,c;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&c);
        for(int i = 0;i < 2;i++) for(int j = 0;j <= n*2+1;j++) ar[i][j].clear();
        for(int i = 0;i < m;i++){
            int a,b;
            ll p;
            scanf("%d%d%I64d",&a,&b,&p);
            p *= p;
            ar[0][a+b-1].push_back(node(a,p));
            ar[1][a-b+n].push_back(node(a,p));
        }

        for(int i = 0;i < 2;i++){
            for(int j = 1;j < 2*n;j++){
                sort(ar[i][j].begin(),ar[i][j].end());
            }
        }

        int ans = 0;
        for(int i = 0;i < 2;i++){
            for(int j = 1;j < 2*n;j++){
                nn = 0;
                for(int k = 0;k < ar[i][j].size();k++){
                    int x;
                    ll p;
                    x = ar[i][j][k].x;
                    p = ar[i][j][k].p;
                    des[nn++] = -p-x;
                    des[nn++] = p-x+c-1;
                }
                sort(des,des+nn);
                nn = unique(des,des+nn)-des;
                //for(int i = 0;i < nn;i++) printf("%d ",des[i]);
                //if(nn) printf("\n");
                init(nn);
                for(int k = 0;k < ar[i][j].size();k++){
                    int x;
                    ll p;
                    x = ar[i][j][k].x;
                    p = ar[i][j][k].p;
                    int tmp,id;
                    id = _find(p-x+c-1);
                    tmp = query(id);
                    id = _find(-p-x);
                    update(id,tmp+1);
                    ans = max(ans,tmp+1);
                }
                //for(int i = 1;i <= nn;i++){
                //    printf("%I64d %d\n",des[nn-i],bit[i]);
                //}
                //if(nn)
                //printf("%d\n\n",ans);
            }
        }

        printf("%d\n",ans);
        //printf("=======================================================================\n");
    }
    return 0;
}


//1.ar数组开小了,re了
//2.lowerbound只能对升序的二分,把des排成降序的,结果没法二分..sb了
//3.des数组开始开成int型的了,应该是ll
//4.公式推错 - - 该打!


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn = 100010;

struct node{
    int x;
    ll p;
    node(int x,ll p):x(x),p(p){}
    node(){}
    bool operator<(const node &a)const{
        if(1ll*x-p==1ll*a.x-a.p) return p+x<a.p+a.x;
        return 1ll*x-p<1ll*a.x-a.p;
    }
};

vector<node> ar[2][maxn<<1];



int main(){
    freopen("bishops.in","r",stdin);
    int T,n,m,c;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&c);
        for(int i = 0;i < 2;i++) for(int j = 0;j <= n*2+1;j++) ar[i][j].clear();
        for(int i = 0;i < m;i++){
            int a,b;
            ll p;
            scanf("%d%d%I64d",&a,&b,&p);
            p *= p;
            ar[0][a+b-1].push_back(node(a,p));
            ar[1][a-b+n].push_back(node(a,p));
        }

        for(int i = 0;i < 2;i++){
            for(int j = 1;j < 2*n;j++){
                sort(ar[i][j].begin(),ar[i][j].end());
            }
        }

        int ans = 0;
        for(int i = 0;i < 2;i++){
            for(int j = 1;j < 2*n;j++){
                ll r;
                int tmp = 0;
                for(int k = 0;k < ar[i][j].size();k++){
                    ll temp = ar[i][j][k].p+ar[i][j][k].x;
                    ll l = 1ll*ar[i][j][k].x-ar[i][j][k].p;
                    //printf("k=%d temp=%I64d r=%I64d\n",k,temp,r);
                    if(k == 0){
                        r = temp;
                        tmp = 1;
                    }
                    else{
                        if(l+1>=r+c){
                            tmp++;
                            r = temp;
                        }
                        else{
                            if(temp < r){
                                r = temp;
                            }
                        }
                    }
                }
                ans = max(ans,tmp);
            }
        }

        printf("%d\n",ans);
        //printf("=======================================================================\n");
    }
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值