[BZOJ1818][Cqoi2010]內部白點
阿新 • • 發佈:2018-12-25
題目連結:
首先,題目根本不會有\(-1\)的情況,且所有節點變色只發生在第一秒。
證明?如果一個節點\((x,y)\)在第二秒變色,那麼一定有一個節點會在第一秒內於\((x,y)\)的四周生成。
假設在左邊(其他方向也一樣),則設座標為\((x`,y)\)。
那麼因為\((x`,y)\)出現了,說明\((x`,y)\)是個內部節點,那麼在此之前\((x`,y)\)的左邊也一定有一個黑色節點,否則此節點不能變色。
那麼\((x,y)\)就可以在第一秒變色。
則題目變成求線段(兩個\(x\)或\(y\)軸座標相同點的連線)的交點數量。
那麼將所有線段離散化,從左向右掃描豎線,對於橫線用樹狀陣列維護當前哪些地方有橫向線段。
若碰到線段左端,此線段對\(y\)座標做出\(1\)的貢獻,反之\(-1\)。
對於如\((1,1),(3,1),(5,1)\)三個點,應該拆成兩條線段,防止覆蓋。
看了看別人的程式碼,感覺自己的好麻煩。。還慢
時間複雜度 \(O(nlog_2n)\)
#include <cstdio> #include <vector> #include <cstring> #include <algorithm> int n,Horc,Verc; int xs[100005],ys[100005]; int as[100005]; std::vector<int> cs[100005],St[100005],Ed[100005]; struct Segment { int l,r,p; }Hor[100005],Ver[100005];//線段,Hor為橫向 struct Binary_Indexed_Tree { int c[100005]; inline void Modify(int x,int v) { for(;x<=n;x+=x&-x)c[x]+=v; } inline int Query(int x) { int Res=0; for(;x;x^=x&-x)Res+=c[x]; return Res; } }BIT;//樹狀陣列 void Simplify(int *a)//離散化 { memcpy(as,a,sizeof as); std::sort(as+1,as+n+1); int nl=std::unique(as+1,as+n+1)-as-1; for(int i=1;i<=n;++i) a[i]=std::lower_bound(as+1,as+nl+1,a[i])-as; } void Getseg(int *a,int *b,int &Cnt,Segment *Tar)//求線段 { for(int i=1;i<=n;++i) cs[i].clear(); for(int i=1;i<=n;++i) cs[b[i]].push_back(a[i]); for(int i=1;i<=n;++i) std::sort(cs[i].begin(),cs[i].end()); for(int i=1;i<=n;++i) for(int j=1;j<(int)cs[i].size();++j) if(cs[i][j]-cs[i][j-1]>=2) Tar[++Cnt]=(Segment){cs[i][j-1]+1,cs[i][j]-1,i}; } int main() { scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d%d",&xs[i],&ys[i]); Simplify(xs); Simplify(ys); Getseg(xs,ys,Horc,Hor); Getseg(ys,xs,Verc,Ver); for(int i=1;i<=Horc;++i) { St[Hor[i].l].push_back(Hor[i].p); Ed[Hor[i].r].push_back(Hor[i].p); } int Sv=1; long long Ans=0; for(int i=1;i<=n;++i) { for(int j=0;j<(int)St[i].size();++j) BIT.Modify(St[i][j],+1);//碰到左端端點 for(;Sv<=Verc&&Ver[Sv].p==i;++Sv) Ans+=BIT.Query(Ver[Sv].r)-BIT.Query(Ver[Sv].l-1); for(int j=0;j<(int)Ed[i].size();++j) BIT.Modify(Ed[i][j],-1);//碰到右端端點 } printf("%lld\n",Ans+n);//還要加上原來的黑點 return 0; }