题意:在1000*1000的格子上,每一个格子的值为(x+A)(y+B)(A,B)为给定的值。现在要查询斜率为b/a,直角点为(x,1)的直角三角形包含的所有格子的点的和,包括边界。
思路:因为只有查询操作,我们可以将其作为离线问题。很容易想到,要对所有的斜率进行极角排序,而对于求和询问,我们可以利用数状数组。
注意到,给定的三角形是固定一个点的,同时在x轴方向,是前缀和。所以,我们只需开一维数组即可,不用二维。
但是如何进行更新操作呢?
我首先想到的是:因为每个点的数值是由公式算出来的,固定X的时候,实质是一个等差数列,可以直接用公式求出。但是这样的复杂度非常高:10^5*1000*lg1000 > 10^8 好像有点高,必须优化。
我们可以发现,对于每次更新操作,如果询问非常密集的话,有些更新为0,这些是没有必要更新的,但是怎么判断呢?
(下面是从网上题解明白的)我们可以对这1000*1000个点进行极角排序,询问的斜率同样要进行排序。从前向后处理询问的时候,我们依次加入极角小于等于询问的点。
这样,总共的复杂度会因为平摊而大大降低。
坑:因为需要打乱排序,我们需要记录原来的编号。但是样例中给出的情况是排好序的,坑爹呀。。。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAX = 1100;
const double EPS = 1e-6;
int dcmp(double x)
{
if(fabs(x) < EPS)
return 0;
else if(x > 0)
return 1;
else
return -1;
}
struct yy{
int x,y;
double t;
int id;
bool operator < (const yy & rhs) const{
return dcmp(t - rhs.t) < 0;
}
} p[MAX * MAX], event[MAX*MAX];
ll sum[MAX];
ll ret[MAX*MAX];
void update(int n, ll data)
{
for(;n <= 1000; n += n &(-n))
sum[n]+= data;
}
ll getsum(int n)
{
ll ans = 0;
for(;n > 0; n -= n &(-n))
ans += sum[n];
return ans;
}
int main(void)
{
//freopen("main.in","r",stdin);
int tot = 0;
for(int i = 1; i <= 1000; ++i){
for(int j = 1; j <= 1000; ++j){
p[tot].x = i, p[tot].y = j;
p[tot++].t = (double) j / i;
}
}
sort(p,p+tot);
int T;
scanf("%d", &T);
for(int cas = 1; cas <= T; ++cas){
ll A,B;
scanf("%I64d %I64d",&A,&B);
int M;
scanf("%d", &M);
for(int i = 1; i <= M; ++i){
double a,b;
scanf("%lf %lf %d", &a, &b,&event[i].x);
event[i].t = b / a;
event[i].id = i;
}
sort(event+1,event + M+1);
printf("Case #%d:\n",cas);
memset(sum,0,sizeof(sum));
int cnt = 0;
for(int i = 1; i <= M; ++i){
for(;cnt < tot && dcmp(p[cnt].t - event[i].t) <= 0;cnt++){
update(p[cnt].x,(p[cnt].x + A) * (p[cnt].y + B));
}
ret[event[i].id] = getsum(event[i].x);
}
for(int i = 1; i <= M; ++i)
printf("%I64d\n",ret[i]);
}
return 0;
}