牛客練習賽29 C題 枇杷
題意:
二維平面的第一象限內,有兩種操作,一是在某個點的權值加一,二是查詢一個直角梯形範圍內的權值的和。
題解:
std解法是CDQ分治,想了個分塊暴力的方法,速度竟然是最快的。。。
將整個第一象限分為128*128(1<<7)個塊,每個塊是邊長為(1<<23)的正方形,這樣可以表示座標(1<<30)範圍內的點,滿足題目給出的範圍
建立一個128*128的二維樹狀陣列,用來求一個矩形區域內的權值的和
對於第一種操作,求出點所在的塊,在二維樹狀陣列中單點修改就行
關鍵是第二種操作
一個直角梯形:
分解為:
矩形和直角三角形分開計算
矩形部分:
先確定矩形的邊界佔據的塊,即求出矩形所在塊的x方向和y方向的範圍:l , r , dn , up
那麼,範圍 l+1 ~ r-1 , dn+1 ~ up -1 內的塊中的點一定是在矩形範圍內的,就是下圖中的綠色部分
綠色部分的權值和用二維樹狀陣列解決,4條邊界所經過的塊就必須要遍歷一遍,對於每一個塊,看塊中包含的點在矩形內的個數,兩部分加一起就是矩形範圍內的權值和
然後是三角形部分
由於不是矩形,無法通過二維樹狀陣列一次求解,方法是遍歷左右區間,即 x 方向在 [ l+1 , r-1 ] ,y方向為 dn+1 的塊,就是綠色塊的底邊,通過直線方程可以計算出當前列的綠色塊的上限,顯然每一列的個數是不同的,這樣一列一列計算出綠色塊包含的權值和(這裡由於是一列,所以可以用一維樹狀陣列求解),再遍歷邊界經過的塊,兩部分加一起就是三角形範圍內的權值和
具體實現起來方法很多,不同方法需要考慮的細節不一樣,下面的方法需要考慮的細節比較少。
複雜度分析
最壞1e5個點,1e4多個塊,由於資料隨機,平均一個塊不到10個點
操作一複雜度為二維樹狀陣列複雜度:log*log,即7*7
操作二複雜度
矩形:一個二維樹狀陣列查詢:log*log,邊界經過的塊的期望值為128個,每個塊平均有10個點,總複雜度為128*10
三角形:邊界經過的塊的期望值為128個,求綠色塊時每一列查詢一次一維樹狀陣列,單次為log,共128*7,邊界複雜度同矩形,為128*10,總複雜度為128*10
所以,操作二複雜度為128*10
由於操作一二是隨機出現,實際複雜度遠遠達不到上限,原因:首先前期每個塊中點的個數遠達不到10,後期個數可能會多一點,但這一定是操作一過多導致的,這又導致操作二變得很少,於是發現真正耗時間的操作二少很多。一次操作姑且期望為 1e3,共有1e5個操作,總用時1e8
程式碼:
#include<bits/stdc++.h>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define mod 998244353
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
struct Point
{
int x,y;
};
vector<Point> a[1<<7][1<<7];
int dd[1<<7][1<<7],f[1<<7][1<<7],op,x,y,xx,d;
inline void update(int x,int y)
{
for (int i=x;i<128;i|=(i+1))
for (int j=y;j<128;j|=(j+1))
dd[i][j]++;
for (int i=y;i<128;i|=(i+1))
f[x][i]++;
}
inline int sum(int x,int y)
{
int res=0;
for (int i=x;i>=0;i=(i&(i+1))-1)
for (int j=y;j>=0;j=(j&(j+1))-1)
res+=dd[i][j];
return res;
}
inline int gsum(int x,int y)
{
int res=0;
for (int i=y;i>=0;i=(i&(i+1))-1)
res+=f[x][i];
return res;
}
inline int spyer(int l,int r,int dn,int up) //矩形
{
if (r>127) r=127;
if (up>127) up=127;
int res=0,t=xx+d+y;
for (int i=l;i<=r;i++)
{
for (int j=0;j<a[i][up].size();j++) //上邊界
{
int tx=a[i][up][j].x,ty=a[i][up][j].y;
if (tx>=x && tx<=xx && ty<=y+d && ty>=y) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
if (dn<up)
for (int j=0;j<a[i][dn].size();j++) //下邊界
{
int tx=a[i][dn][j].x,ty=a[i][dn][j].y;
if (tx>=x && tx<=xx && ty>=y && ty<=y+d) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
}
for (int i=dn+1;i<up;i++)
{
for (int j=0;j<a[l][i].size();j++) //左邊界
{
int tx=a[l][i][j].x,ty=a[l][i][j].y;
if (tx>=x && tx<=xx && ty>=y && ty<=y+d) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
if (l<r)
for (int j=0;j<a[r][i].size();j++) //右邊界
{
int tx=a[r][i][j].x,ty=a[r][i][j].y;
if (tx>=x && tx<=xx && ty>=y && ty<=y+d) res++;else
if (tx>xx && tx<=xx+d && ty>=y && tx+ty<=t) res++;
}
}
l++;r--;dn++;up--;
if (l>r || dn>up) return res;else
return res+sum(r,up)-sum(l-1,up)-sum(r,dn-1)+sum(l-1,dn-1);
}
inline int spyerr() // 三角形
{
int res=0;
int nx=((xx>>23)+1)<<23,t=xx+d+y; //三角形的左邊界在矩形上其實已經算過了,重新確定需要計算的左邊界,一定是某一個塊的起始位置
xx+=d;
if (nx>xx) return 0;
if ((nx>>23)>127) return 0;
int l=nx>>23,r=xx>>23,dn=y>>23;
for (int i=l;i<=r;i++) // 計算下邊界
{
for (int j=0;j<a[i][dn].size();j++)
{
int tx=a[i][dn][j].x,ty=a[i][dn][j].y;
if (tx>=nx && tx<=xx && ty>=y && tx+ty<=t) res++;
}
}
int ny=(dn+1)<<23; y=t-nx; //重新確定需要計算的下邊界,一定是某一個塊的起始位置
xx=t-ny; r=xx>>23;
if (ny>y) return res;
for (int i=l;i<=r;i++)
{
int tx=i<<23,ttx=((i+1)<<23)-1,ty=t-tx,tty=t-ttx,t1=ty>>23,t2=tty>>23; //當前列的塊的座標左右邊界所處的塊為t1,t2
if (t1<128) for (int j=0;j<a[i][t1].size();j++) // 斜邊經過的塊的計算
{
int tx=a[i][t1][j].x,ty=a[i][t1][j].y;
if (tx>=nx && tx<=xx && ty>=ny && tx+ty<=t) res++;
}
if (t2!=t1 && t2>dn && t2<128)
for (int j=0;j<a[i][t2].size();j++) // 斜邊經過的塊的計算
{
int tx=a[i][t2][j].x,ty=a[i][t2][j].y;
if (tx>=nx && tx<=xx && ty>=ny && tx+ty<=t) res++;
}
if (t2>127) t2=128;
if (t2-1>dn)res+=gsum(i,t2-1)-gsum(i,dn); //綠色的塊計算
}
return res;
}
int main()
{
int n;
sc(n);
for (int i=1;i<=n;i++)
{
sc(op);
if (op==1)
{
scc(x,y);
a[x>>23][y>>23].pb(Point{x,y});
update(x>>23,y>>23);
}else
{
scc(x,y); scc(xx,d);
int tmp=spyer(x>>23,xx>>23,y>>23,(y+d)>>23);
tmp+=spyerr();
printf("%d\n",tmp);
}
}
}