嘟嘟嘟
都说这题是送分题,但我怎么就不觉得的呢。
看来我还是太弱了啊……
大体思路就是对于每一个设计方案,答案就是每一个关键点能更新的点的数量之和。
关键在于怎么求一个关键点能更新那些点。
首先这些点肯定是一个包含关键点(a_i)的连续区间,于是可以二分找区间的左右端点。
具体是这样的:
对于一个点(x),判断这个点能否被(a_i)更新。
令(d = |x - a_i|),即(x)和(a_i)间边的数量。然后看从七点出发经过([x - d, x + d])这个区间的所有关键点到(x)的距离有没有比(a_i)小的,如果没有,就说明(x)能被(a_i)更新。
这个倒也好理解,按照算法流程,对于每一个关键点,肯定是向两侧的点 一条边一条边更新的,而如果两个关键点同时更新到一个点的话,这个点一定取距离更小的点。
怎么高效的判断呢?也就是怎么快速的求出从起点出发经过一个区间内的所有关键点到指定点的距离的最小值。
首先预处理两点间距离前缀和。
如果(a_i)在(x)左面,那么这个距离等于(l[a_i] + sum[x] - sum[a_i] = sum[x] + (l[a_i] - sum[a_i]));如果在右面,距离就是(l[a_i] + sum[a_i] - sum[x] = -sum[x] + (l[a_i] + sum[a_i]))。
发现括号里的两个东西可以用st表维护(或线段树)。代码中是维护了(l[a_i] + sum[a_i])的最小值和(sum[a_i] - l[a_i])的最大值。
还有一个细节在于可能有两个关键点到(x)的距离相同,为了防止重复统计,强制规定编号小的点的距离更小。
复杂度(O(nlog ^ 2n)),loj上很愉快的过了,某谷上非得开O2。
别忘开long long。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e5 + 5;
const int N = 18;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int n, m, K;
int lg[maxn];
ll sum[maxn];
struct Edge
{
int p; ll w;
In bool operator < (const Edge& oth)const
{
return p < oth.p;
}
}e[maxn];
ll dis1[maxn], dis2[maxn];
int Min[maxn][N + 2], Max[maxn][N + 2];
In int _min(int a,int b)
{
if(dis1[a] == dis1[b]) return min(a, b);
return dis1[a] < dis1[b] ? a : b;
}
In int _max(int a, int b)
{
if(dis2[a] == dis2[b]) return max(a, b);
return dis2[a] > dis2[b] ? a : b;
}
In void init()
{
sort(e + 1, e + K + 1);
for(int i = 1; i <= K; ++i)
{
dis1[i] = e[i].w + sum[e[i].p], dis2[i] = sum[e[i].p] - e[i].w;
Min[i][0] = Max[i][0] = i;
}
for(int j = 1; (1 << j) <= K; ++j)
for(int i = 1; i + (1 << j) - 1 <= K; ++i)
{
Min[i][j] = _min(Min[i][j - 1], Min[i + (1 << (j - 1))][j - 1]);
Max[i][j] = _max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]);
}
}
In int query_Min(int L, int R)
{
int k = lg[R - L + 1];
return _min(Min[L][k], Min[R - (1 << k) + 1][k]);
}
In int query_Max(int L, int R)
{
int k = lg[R - L + 1];
return _max(Max[L][k], Max[R - (1 << k) + 1][k]);
}
In int update(int a, int b, int x)
{
ll ans1 = e[a].w + abs(sum[x] - sum[e[a].p]);
ll ans2 = e[b].w + abs(sum[x] - sum[e[b].p]);
if(ans1 ^ ans2) return ans1 < ans2 ? a : b;
int nod1 = abs(e[a].p - x), nod2 = abs(e[b].p - x);
if(nod1 ^ nod2) return nod1 < nod2 ? a : b;
return min(a, b);
}
In bool judge(int x, int t)
{
int d = abs(x - e[t].p), ret = t;
int l = lower_bound(e + 1, e + K + 1, (Edge){x - d, 0}) - e;
int r = upper_bound(e + 1, e + K + 1, (Edge){x + d, 0}) - e - 1;
int mid = upper_bound(e + 1, e + K + 1, (Edge){x, 0}) - e - 1;
if(l <= mid) ret = update(ret, query_Max(l, mid), x);
if(mid < r) ret = update(ret, query_Min(mid + 1, r), x);
return ret == t;
}
In ll solve()
{
ll ret = 0;
for(int i = 1, ansL, ansR; i <= K; ++i)
{
int L = 1, R = e[i].p, mid;
while(L < R)
if(judge(mid = (L + R) >> 1, i)) R = mid;
else L = mid + 1;
ansL = L;
L = e[i].p, R = n;
while(L < R)
if(judge(mid = (L + R + 1) >> 1, i)) L = mid;
else R = mid - 1;
ansR = L;
ret += ansR - ansL + 1;
}
return ret;
}
int main()
{
#ifdef mrclr
freopen("t2.in", "r", stdin);
freopen("ha.out", "w", stdout);
#endif
n = read(), m = read();
for(int i = 2; i < maxn; ++i) lg[i] = lg[i >> 1] + 1;
for(int i = 2; i <= n; ++i) sum[i] = read(), sum[i] += sum[i - 1];
for(int i = 1; i <= m; ++i)
{
K = read();
for(int j = 1; j <= K; ++j) e[j].p = read(), e[j].w = read();
init();
write(solve()), enter;
}
return 0;
}