题意:
一个n*n的棋盘,有m个主教。每个主教都有自己的权值p。给出一个值C,在棋盘中找到一个最大点集。这个点集中的点在同一条对角线上且对于点集中任意两点(i,j),i和j之间的主教数(包括i,j)不小于pi^2+pj^2+C。
题解:
对角线有2个方向,每个方向有2*n-1条对角线。一共时4*n-2条。每个点都在2条对角线上。
对于在同一对角线上的点(i,j)若满足i-j+1>=pi^2+pj^2+C即-j-pj^2>=pi^2+C-i-1(i>j)即可。
那么将同一对角线上的点按x坐标排序。将每个点的信息整合到一起离散化,最后用树状数组维护每个点前面满足-j-pj^2>=pi^2+C-i-1的最大值。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, ll> P; const int maxn = 1e5+10; int t; int n, m; ll c; int u, v; ll p; int cnt; int ans; ll _sort[maxn<<1]; int tree[maxn<<1]; struct node { int x; ll val; node(int a, ll b) { x = a; val = b; } }; vector<P> bs[2][maxn<<1]; void update(int x, int val) { while(x <= cnt) { tree[x] = max(tree[x], val); x += x&(-x); } } int sum(int x) { int res = 0; while(x > 0) { res = max(res, tree[x]); x -= x&(-x); } return res; } int main() { freopen("bishops.in","r",stdin); scanf("%d", &t); while(t--) { ans = 0; scanf("%d%d%lld", &n, &m, &c); for(int i = 0; i < 2; i++) for(int j = 1; j < 2*n; j++) bs[i][j].clear(); while(m--) { scanf("%d%d%lld", &u, &v, &p); p *= p; bs[0][u+v-1].push_back(P(u, p)); bs[1][u-v+n].push_back(P(u, p)); } for(int i = 0; i < 2; i++) for(int j = 1; j < 2*n; j++) sort(bs[i][j].begin(), bs[i][j].end()); for(int i = 0; i < 2; i++) for(int j = 1; j < 2*n; j++) { int len = bs[i][j].size(); cnt = 0; for(int k = 0; k < len; k++) { int x = bs[i][j][k].first; ll p = bs[i][j][k].second; _sort[cnt++] = -p-x; _sort[cnt++] = p+c-x-1; } sort(_sort, _sort+cnt); cnt = unique(_sort, _sort+cnt)-_sort; for(int k = 1; k <= cnt; k++) tree[k] = 0; for(int k = 0; k < len; k++) { int x = bs[i][j][k].first; ll p = bs[i][j][k].second; int id = cnt-(lower_bound(_sort, _sort+cnt, p+c-x-1)-_sort); int tmp = sum(id); ans = max(ans, tmp+1); id = cnt-(lower_bound(_sort, _sort+cnt, -p-x)-_sort); update(id, tmp+1); } } printf("%d ", ans); } }