题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4082
题意:题目给k个点(x,y),所有点1<=x<=n,1<=y<=n,然后q次询问,一个点p的权值为所有在p左下方的点到p的曼哈顿距离(x差值和y差值的和)和,一共q次询问,每次给一个常数c,求点P个数,P(x,y)满足1<=x<=n,1<=y<=n,且到给出的k个点距离和的值等于c的点的个数。
思路:一开始一直想着用线段树维护,然后发现很复杂......然后自闭,看了题解之后发现确实可以用可持续化线段树,但是可以巧解,于是果断选择巧解...可持续化线段树写不来的......按x(或者按y都一样)遍历坐标,维护该横坐标上所有y坐标对应的左侧点个数cnt[y]和对应横纵坐标和sum[y],然后放一个res从n开始往下扫,如果满足cnt*(x+y)-sum=c,那就找到了,然后横坐标往右扫,每个横坐标上最多一个满足情况的值,如果大于c就把res往下,小于c就横坐标往后扫
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const int maxn = 1e5 + 50;
#define INF 0xffffff
typedef struct node
{
int x, y;
}node;
int n , k, q;
ll c[15];
node p[maxn];
int cnt[maxn];
ll sum[maxn];
bool cmp(node a, node b)
{
if(a.x==b.x)
return a.y>b.y;
return a.x<b.x;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
for(int i=0;i<k;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
}
sort(p,p+k,cmp);
scanf("%d",&q);
for(int i=0;i<q;i++)
scanf("%lld",&c[i]);
for(int z=0;z<q;z++)
{
int id = 0;
ll res = n;
int ans = 0;
ll x = 0, y = 0;
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
if(res<1)break;
while(id<k && p[id].x==i)
{
if(p[id].y>res)
{
id++;
continue;
}
cnt[p[id].y]++;
x++;
sum[p[id].y]+=(i+p[id].y);
y+=(i+p[id].y);
id++;
}
while(1ll*x*(i+res)-1ll*y>c[z])
{
if(cnt[res])
{
x-=cnt[res];
y-=sum[res];
}
res--;
}
if(res<1)break;
if(x*(i+res)-y==c[z])
{
ans++;
if(cnt[res])
{
x-=cnt[res];
y-=sum[res];
}
res--;
}
}
if(z<q-1)
printf("%d ",ans);
else
printf("%d\n",ans);
}
}
}